/*******************************************************************************
 * Copyright (c) 2000, 2023 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Timo Kinnunen - Contributions for bug 377373 - [subwords] known limitations with JDT 3.8
 *     							Bug 420953 - [subwords] Constructors that don't match prefix not found
 *     IBM Corporation - initial API and implementation
 *     Stephan Herrmann - Contribution for
 *								Bug 400874 - [1.8][compiler] Inference infrastructure should evolve to meet JLS8 18.x (Part G of JSR335 spec)
 *     Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
 *     Microsoft Corporation - Contribution for bug 575562 - improve completion search performance
 *     							Bug 578817 - skip the ignored types before creating CompletionProposals
 *     Microsoft Corporation - Contribution for bug 578815 - more candidates when completion after new keyword for an interface
 *******************************************************************************/
package org.eclipse.jdt.internal.codeassist;

import static org.eclipse.jdt.internal.core.JavaModelManager.trace;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.CompletionContext;
import org.eclipse.jdt.core.CompletionFlags;
import org.eclipse.jdt.core.CompletionProposal;
import org.eclipse.jdt.core.CompletionRequestor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IModuleDescription;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.codeassist.complete.AssistNodeParentAnnotationArrayInitializer;
import org.eclipse.jdt.internal.codeassist.complete.CompletionJavadoc;
import org.eclipse.jdt.internal.codeassist.complete.CompletionNode;
import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeDetector;
import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeFound;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnAnnotationOfType;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnArgumentName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnBreakStatement;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnClassLiteralAccess;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnContinueStatement;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnExplicitConstructorCall;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnFieldName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnFieldType;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnImportReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadoc;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocAllocationExpression;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocFieldReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocMessageSend;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocModuleReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocParamNameReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocTag;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnJavadocTypeParamReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeyword3;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeywordModuleDeclaration;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnKeywordModuleInfo;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnLocalName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMarkerAnnotationName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberValueName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMessageSend;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMessageSendName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMethodName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMethodReturnType;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnModuleDeclaration;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnModuleReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnPackageReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnPackageVisibilityReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesImplementationsQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesImplementationsSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesInterfacesQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnProvidesInterfacesSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedAllocationExpression;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnRecordComponentName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnReferenceExpressionName;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnStringLiteral;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnUsesQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnUsesSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionParser;
import org.eclipse.jdt.internal.codeassist.complete.CompletionScanner;
import org.eclipse.jdt.internal.codeassist.complete.InvalidCursorLocation;
import org.eclipse.jdt.internal.codeassist.impl.AssistParser;
import org.eclipse.jdt.internal.codeassist.impl.Engine;
import org.eclipse.jdt.internal.codeassist.impl.Keywords;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ExtraFlags;
import org.eclipse.jdt.internal.compiler.ast.AND_AND_Expression;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.eclipse.jdt.internal.compiler.ast.ArrayReference;
import org.eclipse.jdt.internal.compiler.ast.AssertStatement;
import org.eclipse.jdt.internal.compiler.ast.Assignment;
import org.eclipse.jdt.internal.compiler.ast.BinaryExpression;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.CaseStatement;
import org.eclipse.jdt.internal.compiler.ast.CastExpression;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConditionalExpression;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.ExpressionContext;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
import org.eclipse.jdt.internal.compiler.ast.ForStatement;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.eclipse.jdt.internal.compiler.ast.InstanceOfExpression;
import org.eclipse.jdt.internal.compiler.ast.Javadoc;
import org.eclipse.jdt.internal.compiler.ast.JavadocImplicitTypeReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocModuleReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleReference;
import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation;
import org.eclipse.jdt.internal.compiler.ast.OperatorExpression;
import org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.eclipse.jdt.internal.compiler.ast.PackageVisibilityStatement;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ProvidesStatement;
import org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ReferenceExpression;
import org.eclipse.jdt.internal.compiler.ast.RequiresStatement;
import org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SuperReference;
import org.eclipse.jdt.internal.compiler.ast.SwitchStatement;
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference;
import org.eclipse.jdt.internal.compiler.ast.UsesStatement;
import org.eclipse.jdt.internal.compiler.ast.WhileStatement;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.env.ISourceType;
import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.eclipse.jdt.internal.compiler.lookup.BaseTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
import org.eclipse.jdt.internal.compiler.lookup.InferenceContext18;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants;
import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.compiler.util.SimpleSetOfCharArray;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.BasicCompilationUnit;
import org.eclipse.jdt.internal.core.BinaryTypeConverter;
import org.eclipse.jdt.internal.core.INamingRequestor;
import org.eclipse.jdt.internal.core.InternalNamingConventions;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.jdt.internal.core.JavaElementRequestor;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.ModuleSourcePathManager;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SourceMethod;
import org.eclipse.jdt.internal.core.SourceMethodElementInfo;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;

/**
 * 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).
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public final class CompletionEngine
	extends Engine
	implements ISearchRequestor, TypeConstants , TerminalTokens , RelevanceConstants, SuffixConstants {

	private static class AcceptedConstructor {
		public int modifiers;
		public char[] simpleTypeName;
		public int parameterCount;
		public char[] signature;
		public char[][] parameterTypes;
		public char[][] parameterNames;
		public int typeModifiers;
		public char[] packageName;
		public int extraFlags;
		public int accessibility;
		public boolean proposeType = false;
		public boolean proposeConstructor = false;
		public char[] fullyQualifiedName = null;

		public boolean mustBeQualified = false;

		public AcceptedConstructor(
				int modifiers,
				char[] simpleTypeName,
				int parameterCount,
				char[] signature,
				char[][] parameterTypes,
				char[][] parameterNames,
				int typeModifiers,
				char[] packageName,
				int extraFlags,
				int accessibility) {
			this.modifiers = modifiers;
			this.simpleTypeName = simpleTypeName;
			this.parameterCount = parameterCount;
			this.signature = signature;
			this.parameterTypes = parameterTypes;
			this.parameterNames = parameterNames;
			this.typeModifiers = typeModifiers;
			this.packageName = packageName;
			this.extraFlags = extraFlags;
			this.accessibility = accessibility;
		}

		@Override
		public String toString() {
			StringBuilder buffer = new StringBuilder();
			buffer.append('{');
			buffer.append(this.packageName);
			buffer.append(',');
			buffer.append(this.simpleTypeName);
			buffer.append('}');
			return buffer.toString();
		}
	}

	private static class AcceptedType {
		public char[] packageName;
		public char[] simpleTypeName;
		public char[][] enclosingTypeNames;
		public int modifiers;
		public int accessibility;
		public boolean mustBeQualified = false;

		public char[] fullyQualifiedName = null;
		public char[] qualifiedTypeName = null;
		public AcceptedType(
			char[] packageName,
			char[] simpleTypeName,
			char[][] enclosingTypeNames,
			int modifiers,
			int accessibility) {
			this.packageName = packageName;
			this.simpleTypeName = simpleTypeName;
			this.enclosingTypeNames = enclosingTypeNames;
			this.modifiers = modifiers;
			this.accessibility = accessibility;
		}

		@Override
		public String toString() {
			StringBuilder buffer = new StringBuilder();
			buffer.append('{');
			buffer.append(this.packageName);
			buffer.append(',');
			buffer.append(this.simpleTypeName);
			buffer.append(',');
			buffer.append(CharOperation.concatWith(this.enclosingTypeNames, '.'));
			buffer.append('}');
			return buffer.toString();
		}
	}

	public class CompletionProblemFactory extends DefaultProblemFactory {
		private int lastErrorStart;

		private boolean checkProblems = false;
		public boolean hasForbiddenProblems = false;
		public boolean hasAllowedProblems = false;

		public CompletionProblemFactory(Locale loc) {
			super(loc);
		}

		private CategorizedProblem checkProblem(CategorizedProblem pb,
			char[] originatingFileName,	int severity, int start) {
			int id = pb.getID();
			if (CompletionEngine.this.actualCompletionPosition > start
				&& this.lastErrorStart < start
				&& pb.isError()
				&& (id & IProblem.Syntax) == 0
				&& (CompletionEngine.this.fileName == null || CharOperation.equals(CompletionEngine.this.fileName, originatingFileName))) {

				CompletionEngine.this.problem = pb;
				this.lastErrorStart = start;
			}
			if (this.checkProblems && !this.hasForbiddenProblems) {
				switch (id) {
					case IProblem.UsingDeprecatedType:
						this.hasForbiddenProblems =
							CompletionEngine.this.options.checkDeprecation;
						break;
					case IProblem.NotVisibleType:
						this.hasForbiddenProblems =
							CompletionEngine.this.options.checkVisibility;
						break;
					case IProblem.ForbiddenReference:
						this.hasForbiddenProblems =
							CompletionEngine.this.options.checkForbiddenReference;
						break;
					case IProblem.DiscouragedReference:
						this.hasForbiddenProblems =
							CompletionEngine.this.options.checkDiscouragedReference;
						break;
					default:
						if ((severity & ProblemSeverities.Optional) != 0) {
							this.hasAllowedProblems = true;
						} else {
							this.hasForbiddenProblems = true;
						}

						break;
				}
			}

			return pb;
		}

		@Override
		public CategorizedProblem createProblem(
				char[] originatingFileName,
				int problemId,
				String[] problemArguments,
				int elaborationId,
				String[] messageArguments,
				int severity,
				int start,
				int end,
				int lineNumber,
				int columnNumber) {
				return checkProblem(
					super.createProblem(
						originatingFileName,
						problemId,
						problemArguments,
						elaborationId,
						messageArguments,
						severity,
						start,
						end,
						lineNumber,
						columnNumber), originatingFileName, severity, start);
		}

		@Override
		public CategorizedProblem createProblem(
				char[] originatingFileName,
				int problemId,
				String[] problemArguments,
				String[] messageArguments,
				int severity,
				int start,
				int end,
				int lineNumber,
				int columnNumber) {
				return checkProblem(
					super.createProblem(
						originatingFileName,
						problemId,
						problemArguments,
						messageArguments,
						severity,
						start,
						end,
						lineNumber,
						columnNumber), originatingFileName, severity, start);
		}

		public void startCheckingProblems() {
			this.checkProblems = true;
			this.hasForbiddenProblems = false;
			this.hasAllowedProblems = false;
		}

		public void stopCheckingProblems() {
			this.checkProblems = false;
		}
	}

	public static char[] createBindingKey(char[] packageName, char[] typeName) {
		char[] signature = createTypeSignature(packageName, typeName);
		CharOperation.replace(signature, '.', '/');
		return signature;
	}

	public static char[][] createDefaultParameterNames(int length) {
		char[][] parameters;
		switch (length) {
			case 0 :
				parameters = new char[length][];
				break;
			case 1 :
				parameters = ARGS1;
				break;
			case 2 :
				parameters = ARGS2;
				break;
			case 3 :
				parameters = ARGS3;
				break;
			case 4 :
				parameters = ARGS4;
				break;
			default :
				parameters = new char[length][];
				for (int i = 0; i < length; i++) {
					parameters[i] = CharOperation.concat(ARG, String.valueOf(i).toCharArray());
				}
				break;
		}
		return parameters;
	}
	public static char[] createMethodSignature(char[][] parameterPackageNames, char[][] parameterTypeNames, char[] returnTypeSignature) {
		char[][] parameterTypeSignature = new char[parameterTypeNames.length][];
		for (int i = 0; i < parameterTypeSignature.length; i++) {
			parameterTypeSignature[i] =
				Signature.createCharArrayTypeSignature(
						CharOperation.concat(
								parameterPackageNames[i],
								CharOperation.replaceOnCopy(parameterTypeNames[i], '.', '$'), '.'), true);
		}

		return Signature.createMethodSignature(
				parameterTypeSignature,
				returnTypeSignature);
	}

	public static char[] createMethodSignature(char[][] parameterPackageNames, char[][] parameterTypeNames, char[] returnPackagename, char[] returnTypeName) {
		char[] returnTypeSignature =
			returnTypeName == null || returnTypeName.length == 0
			? Signature.createCharArrayTypeSignature(VOID, true)
			: Signature.createCharArrayTypeSignature(
					CharOperation.concat(
							returnPackagename,
							CharOperation.replaceOnCopy(returnTypeName, '.', '$'), '.'), true);

		return createMethodSignature(
				parameterPackageNames,
				parameterTypeNames,
				returnTypeSignature);
	}
	public static char[] createNonGenericTypeSignature(char[] qualifiedPackageName, char[] qualifiedTypeName) {
		return Signature.createCharArrayTypeSignature(
				CharOperation.concat(
						qualifiedPackageName,
						CharOperation.replaceOnCopy(qualifiedTypeName, '.', '$'), '.'), true);
	}

	public static char[] createTypeSignature(char[] qualifiedPackageName, char[] qualifiedTypeName) {
		char[] name = new char[qualifiedTypeName.length];
		System.arraycopy(qualifiedTypeName, 0, name, 0, qualifiedTypeName.length);

		int depth = 0;
		int length = name.length;
		for (int i = length -1; i >= 0; i--) {
			switch (name[i]) {
				case '.':
					if (depth == 0 && name[i - 1] != '>') {
						name[i] = '$';
					}
					break;
				case '<':
					depth--;
					break;
				case '>':
					depth++;
					break;
			}
		}
		return Signature.createCharArrayTypeSignature(
				CharOperation.concat(
						qualifiedPackageName,
						name, '.'), true);
	}

	private static char[] getRequiredTypeSignature(TypeBinding typeBinding) {
		char[] result = null;
		StringBuilder sig = new StringBuilder(10);

		sig.append(typeBinding.signature());

		int sigLength = sig.length();
		result = new char[sigLength];
		sig.getChars(0, sigLength, result, 0);
		result = CharOperation.replaceOnCopy(result, '/', '.');
		return result;
	}

	private static char[] getTypeName(TypeReference typeReference) {
		char[] typeName = CharOperation.concatWith(typeReference.getTypeName(), '.');
		int dims = typeReference.dimensions();
		if (dims > 0) {
			int length = typeName.length;
			int newLength = length + (dims*2);
			System.arraycopy(typeName, 0, typeName = new char[newLength], 0, length);
			for (int k = length; k < newLength; k += 2) {
				typeName[k] = '[';
				typeName[k+1] = ']';
			}
		}

		return typeName;
	}

	private static boolean hasStaticMemberTypes(ReferenceBinding typeBinding, SourceTypeBinding invocationType, CompilationUnitScope unitScope) {
		ReferenceBinding[] memberTypes = typeBinding.memberTypes();
		int length = memberTypes == null ? 0 : memberTypes.length;
		next : for (int i = 0; i < length; i++) {
			ReferenceBinding memberType = memberTypes[i];
			if (invocationType != null && !memberType.canBeSeenBy(typeBinding, invocationType)) {
				continue next;
			} else if(invocationType == null && !memberType.canBeSeenBy(unitScope.fPackage)) {
				continue next;
			}

			if ((memberType.modifiers & ClassFileConstants.AccStatic) != 0) {
				return true;
			}
		}
		return false;
	}

	private static boolean hasMemberTypesInEnclosingScope(SourceTypeBinding typeBinding, Scope scope) {
		ReferenceBinding[] memberTypes = typeBinding.memberTypes();
		int length = memberTypes == null ? 0 : memberTypes.length;

		if (length > 0) {
			MethodScope methodScope = scope.methodScope();
			if (methodScope != null && !methodScope.isStatic) {
				ClassScope classScope = typeBinding.scope;
				Scope currentScope = scope;
				while (currentScope != null) {
					if (currentScope == classScope) {
						return true;
					}
					currentScope = currentScope.parent;
				}
			}
		}
		return false;
	}

	public HashtableOfObject typeCache;
	public int openedBinaryTypes; // used during InternalCompletionProposal#findConstructorParameterNames()

	public static boolean DEBUG = false;
	public static boolean PERF = false;

	private static final char[] KNOWN_TYPE_WITH_UNKNOWN_CONSTRUCTORS = new char[]{};
	private static final char[] KNOWN_TYPE_WITH_KNOWN_CONSTRUCTORS = new char[]{};

	private static final char[] ARG = "arg".toCharArray();  //$NON-NLS-1$
	private static final char[] ARG0 = "arg0".toCharArray();  //$NON-NLS-1$
	private static final char[] ARG1 = "arg1".toCharArray();  //$NON-NLS-1$
	private static final char[] ARG2 = "arg2".toCharArray();  //$NON-NLS-1$
	private static final char[] ARG3 = "arg3".toCharArray();  //$NON-NLS-1$
	private static final char[][] ARGS1 = new char[][]{ARG0};
	private static final char[][] ARGS2 = new char[][]{ARG0, ARG1};
	private static final char[][] ARGS3 = new char[][]{ARG0, ARG1, ARG2};
	private static final char[][] ARGS4 = new char[][]{ARG0, ARG1, ARG2, ARG3};

	private final static int CHECK_CANCEL_FREQUENCY = 50;

	// temporary constants to quickly disabled polish features if necessary
	public final static boolean NO_TYPE_COMPLETION_ON_EMPTY_TOKEN = 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[] { ';' };

	private final static char[] CLASS = "Class".toCharArray();  //$NON-NLS-1$
	private final static char[] VOID = "void".toCharArray();  //$NON-NLS-1$
	private final static char[] INT = "int".toCharArray();  //$NON-NLS-1$
	private final static char[] INT_SIGNATURE = new char[]{Signature.C_INT};
	private final static char[] VALUE = "value".toCharArray();  //$NON-NLS-1$
	private final static char[] EXTENDS = "extends".toCharArray();  //$NON-NLS-1$
	private final static char[] SUPER = "super".toCharArray();  //$NON-NLS-1$
	private final static char[] DEFAULT_CONSTRUCTOR_SIGNATURE = "()V".toCharArray();  //$NON-NLS-1$

	private final static char[] DOT = ".".toCharArray();  //$NON-NLS-1$

	private final static char[] VARARGS = "...".toCharArray();  //$NON-NLS-1$

	private final static char[] IMPORT = "import".toCharArray();  //$NON-NLS-1$
	private final static char[] STATIC = "static".toCharArray();  //$NON-NLS-1$
	private final static char[] ON_DEMAND = ".*".toCharArray();  //$NON-NLS-1$
	private final static char[] IMPORT_END = ";\n".toCharArray();  //$NON-NLS-1$
	private final static char[] LAMBDA = "->".toCharArray(); //$NON-NLS-1$

	private final static char[] JAVA_LANG_OBJECT_SIGNATURE =
		createTypeSignature(CharOperation.concatWith(JAVA_LANG, '.'), OBJECT);
	private final static char[] JAVA_LANG_NAME =
		CharOperation.concatWith(JAVA_LANG, '.');
	private final static char[] GET = "get".toCharArray(); //$NON-NLS-1$
	private final static char[] IS = "is".toCharArray(); //$NON-NLS-1$

	private final static int NONE = 0;
	private final static int SUPERTYPE = 1;
	private final static int SUBTYPE = 2;

	int expectedTypesPtr = -1;
	TypeBinding[] expectedTypes = new TypeBinding[1];
	int expectedTypesFilter;
	boolean hasJavaLangObjectAsExpectedType = false;
	boolean hasExpectedArrayTypes = false;
	boolean hasComputedExpectedArrayTypes = false;
	int uninterestingBindingsPtr = -1;
	Binding[] uninterestingBindings = new Binding[1];
	int forbbidenBindingsPtr = -1;
	Binding[] forbbidenBindings = new Binding[1];
	int uninterestingBindingsFilter;     // only set when completing on an exception type

	ImportBinding[] favoriteReferenceBindings;

	boolean assistNodeIsClass;
	boolean assistNodeIsEnum;
	boolean assistNodeIsException;
	boolean assistNodeIsInterface;
	boolean assistNodeIsAnnotation;
	boolean assistNodeIsConstructor;
	boolean assistNodeIsSuperType;
	boolean assistNodeIsExtendedType;
	boolean assistNodeIsInterfaceExcludingAnnotation; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=310423
	int  assistNodeInJavadoc = 0;
	boolean assistNodeCanBeSingleMemberAnnotation = false;
	boolean assistNodeIsInsideCase = false; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=195346
	boolean assistNodeIsString = false;	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=343476

	long targetedElement;

	WorkingCopyOwner owner;
	IProgressMonitor monitor;
	IJavaProject javaProject;
	ITypeRoot typeRoot;
	CompletionParser parser;
	CompletionRequestor requestor;
	CompletionProblemFactory problemFactory;
	ProblemReporter problemReporter;
	private INameEnvironment noCacheNameEnvironment;
	char[] source;
	ModuleDeclaration moduleDeclaration;
	boolean skipDefaultPackage = false;
	char[] completionToken;

	char[] qualifiedCompletionToken;
	boolean resolvingImports = false;
	boolean resolvingStaticImports = false;
	boolean insideQualifiedReference = false;
	boolean noProposal = true;
	CategorizedProblem problem = null;
	char[] fileName = null;
	int startPosition, actualCompletionPosition, endPosition, offset;
	int tokenStart, tokenEnd;
	int javadocTagPosition; // Position of previous tag while completing in javadoc
	String sourceLevel;
	String complianceLevel;
	SimpleSetOfCharArray validPackageNames = new SimpleSetOfCharArray(10);
	SimpleSetOfCharArray invalidPackageNames = new SimpleSetOfCharArray(1);
	HashtableOfObject knownModules = new HashtableOfObject(10);
	HashtableOfObject knownPkgs = new HashtableOfObject(10);
	HashtableOfObject knownTypes = new HashtableOfObject(10);

 	/*
		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 BaseTypeBinding[] BASE_TYPES = {
		TypeBinding.BOOLEAN,
		TypeBinding.BYTE,
		TypeBinding.CHAR,
		TypeBinding.DOUBLE,
		TypeBinding.FLOAT,
		TypeBinding.INT,
		TypeBinding.LONG,
		TypeBinding.SHORT,
		TypeBinding.VOID
	};
	static final int BASE_TYPES_LENGTH = BASE_TYPES.length;
	static final char[][] BASE_TYPE_NAMES = new char[BASE_TYPES_LENGTH][];
	static final int BASE_TYPES_WITHOUT_VOID_LENGTH = BASE_TYPES.length - 1;
	static final char[][] BASE_TYPE_NAMES_WITHOUT_VOID = new char[BASE_TYPES_WITHOUT_VOID_LENGTH][];
	static {
 		for (int i=0; i<BASE_TYPES_LENGTH; i++) {
 			BASE_TYPE_NAMES[i] = BASE_TYPES[i].simpleName;
 		}
		for (int i=0; i<BASE_TYPES_WITHOUT_VOID_LENGTH; i++) {
			BASE_TYPE_NAMES_WITHOUT_VOID[i] = BASE_TYPES[i].simpleName;
		}
 	}

	static final char[] classField = "class".toCharArray();  //$NON-NLS-1$
	static final char[] lengthField = "length".toCharArray();  //$NON-NLS-1$
	static final char[] cloneMethod = "clone".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(){
		@Override
		public TypeBinding[] genericTypeArguments() { return null; }
		@Override
		public boolean isSuperAccess(){ return false; }
		@Override
		public boolean isTypeAccess(){ return false; }
		@Override
		public void setActualReceiverType(ReferenceBinding receiverType) {/* empty */}
		@Override
		public void setDepth(int depth){/* empty */}
		@Override
		public void setFieldIndex(int depth){/* empty */}
		@Override
		public int sourceEnd() { return 0; 	}
		@Override
		public int sourceStart() { return 0; 	}
		@Override
		public TypeBinding invocationTargetType() { return null; }
		@Override
		public boolean receiverIsImplicitThis() { return false; }
		@Override
		public InferenceContext18 freshInferenceContext(Scope scope) { return null; }
		@Override
		public ExpressionContext getExpressionContext() { return ExpressionContext.VANILLA_CONTEXT; }
		@Override
		public boolean isQualifiedSuper() { return false; }
		@Override
		public boolean checkingPotentialCompatibility() { return false; }
		@Override
		public void acceptPotentiallyCompatibleMethods(MethodBinding[] methods) {/* ignore */}
	};

	private int foundTypesCount;
	private ObjectVector acceptedTypes;

	private int foundConstructorsCount;
	private ObjectVector acceptedConstructors;
	/**
	 * The strictMatchForExtepectedType used to skip all elements found at <code>findVariablesAndMethods</code>
	 * which doesn't match the current expected types in the engine in <code>expectedTypes</code>. Since in this mode
	 * all elements found matches the expected type, the completion proposals will not contains the calculated expected type
	 * relevance. This is done to keep the overloaded method suggestions always on top in this mode as a fix for
	 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=575149
	 */
	private boolean strictMatchForExtepectedType = false;

	/**
	 * 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(
			SearchableEnvironment nameEnvironment,
			CompletionRequestor requestor,
			Map settings,
			IJavaProject javaProject,
			WorkingCopyOwner owner,
			IProgressMonitor monitor) {
		super(settings);
		this.javaProject = javaProject;
		this.requestor = requestor;
		this.nameEnvironment = nameEnvironment;
		this.typeCache = new HashtableOfObject(5);
		this.openedBinaryTypes = 0;
		this.sourceLevel = javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
		this.complianceLevel = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);

		this.problemFactory = new CompletionProblemFactory(Locale.getDefault());
		this.problemReporter = new ProblemReporter(
				DefaultErrorHandlingPolicies.proceedWithAllProblems(),
				this.compilerOptions,
				this.problemFactory);
		this.lookupEnvironment =
			new LookupEnvironment(this, this.compilerOptions, this.problemReporter, nameEnvironment);
		this.parser =
			new CompletionParser(this.problemReporter, this.requestor.isExtendedContextRequired(), monitor);
		this.owner = owner;
		this.monitor = monitor;
	}

	@Override
	public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) {
		if (!CharOperation.equals(sourceUnit.getMainTypeName(), TypeConstants.PACKAGE_INFO_NAME)) {
			// do not accept package-info.java as a type for completion engine
			// because it contains no extra info that will help in completion
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=343865
			// Required after the fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=337868
			// because now we get a type corresponding to the package-info.java from the java model.
			super.accept(sourceUnit, accessRestriction);
		}
	}

	@Override
	public void acceptConstructor(
			int modifiers,
			char[] simpleTypeName,
			int parameterCount,
			char[] signature,
			char[][] parameterTypes,
			char[][] parameterNames,
			int typeModifiers,
			char[] packageName,
			int extraFlags,
			String path,
			AccessRestriction accessRestriction) {

		// does not check cancellation for every types to avoid performance loss
		if ((this.foundConstructorsCount % (CHECK_CANCEL_FREQUENCY)) == 0) checkCancel();
		this.foundConstructorsCount++;

		if ((typeModifiers & ClassFileConstants.AccEnum) != 0) return;

		if (this.options.checkDeprecation && (typeModifiers & ClassFileConstants.AccDeprecated) != 0) return;

		if (this.options.checkVisibility) {
			if((typeModifiers & ClassFileConstants.AccPublic) == 0) {
				if((typeModifiers & ClassFileConstants.AccPrivate) != 0) return;

				if (this.currentPackageName == null) {
					initializePackageCache();
				}

				if(!CharOperation.equals(packageName, this.currentPackageName)) return;
			}
		}

		int accessibility = IAccessRule.K_ACCESSIBLE;
		if(accessRestriction != null) {
			switch (accessRestriction.getProblemId()) {
				case IProblem.ForbiddenReference:
					if (this.options.checkForbiddenReference) {
						return;
					}
					accessibility = IAccessRule.K_NON_ACCESSIBLE;
					break;
				case IProblem.DiscouragedReference:
					if (this.options.checkDiscouragedReference) {
						return;
					}
					accessibility = IAccessRule.K_DISCOURAGED;
					break;
			}
		}

		if(this.acceptedConstructors == null) {
			this.acceptedConstructors = new ObjectVector();
		}
		this.acceptedConstructors.add(
				new AcceptedConstructor(
						modifiers,
						simpleTypeName,
						parameterCount,
						signature,
						parameterTypes,
						parameterNames,
						typeModifiers,
						packageName,
						extraFlags,
						accessibility));
	}

	private void acceptConstructors(Scope scope) {
		final boolean DEFER_QUALIFIED_PROPOSALS = false;

		this.checkCancel();

		if(this.acceptedConstructors == null) return;

		int length = this.acceptedConstructors.size();

		if(length == 0) return;

		HashtableOfObject onDemandFound = new HashtableOfObject();

		ArrayList deferredProposals = null;
		if (DEFER_QUALIFIED_PROPOSALS) {
			deferredProposals = new ArrayList();
		}

		try {
			next : for (int i = 0; i < length; i++) {

				// does not check cancellation for every types to avoid performance loss
				if ((i % CHECK_CANCEL_FREQUENCY) == 0) checkCancel();

				AcceptedConstructor acceptedConstructor = (AcceptedConstructor)this.acceptedConstructors.elementAt(i);
				final int typeModifiers = acceptedConstructor.typeModifiers;
				final char[] packageName = acceptedConstructor.packageName;
				final char[] simpleTypeName = acceptedConstructor.simpleTypeName;
				final int modifiers = acceptedConstructor.modifiers;
				final int parameterCount = acceptedConstructor.parameterCount;
				final char[] signature = acceptedConstructor.signature;
				final char[][] parameterTypes = acceptedConstructor.parameterTypes;
				final char[][] parameterNames = acceptedConstructor.parameterNames;
				final int extraFlags = acceptedConstructor.extraFlags;
				final int accessibility = acceptedConstructor.accessibility;

				boolean proposeType = hasArrayTypeAsExpectedSuperTypes() || (extraFlags & ExtraFlags.HasNonPrivateStaticMemberTypes) != 0;

				char[] fullyQualifiedName = CharOperation.concat(packageName, simpleTypeName, '.');

				Object knownTypeKind = this.knownTypes.get(fullyQualifiedName);
				if (knownTypeKind != null) {
					if (knownTypeKind == KNOWN_TYPE_WITH_KNOWN_CONSTRUCTORS) {
						// the type and its constructors are already accepted
						continue next;
					}
					// this type is already accepted
					proposeType = false;
				} else {
					this.knownTypes.put(fullyQualifiedName, KNOWN_TYPE_WITH_UNKNOWN_CONSTRUCTORS);
				}

				boolean proposeConstructor = true;

				if (this.options.checkVisibility) {
					if((modifiers & ClassFileConstants.AccPublic) == 0) {
						if((modifiers & ClassFileConstants.AccPrivate) != 0) {
							if (!proposeType) continue next;
							proposeConstructor = false;
						} else {
							if (this.currentPackageName == null) {
								initializePackageCache();
							}

							if(!CharOperation.equals(packageName, this.currentPackageName)) {

								if((typeModifiers & ClassFileConstants.AccAbstract) == 0 ||
										(modifiers & ClassFileConstants.AccProtected) == 0) {
									if (!proposeType) continue next;
									proposeConstructor = false;
								}
							}
						}
					}
				}

				acceptedConstructor.fullyQualifiedName = fullyQualifiedName;
				acceptedConstructor.proposeType = proposeType;
				acceptedConstructor.proposeConstructor = proposeConstructor;


				if(!this.importCachesInitialized) {
					initializeImportCaches();
				}

				for (int j = 0; j < this.importCacheCount; j++) {
					char[][] importName = this.importsCache[j];
					if(CharOperation.equals(simpleTypeName, importName[0])) {
						if (proposeType) {
							proposeType(
									packageName,
									simpleTypeName,
									typeModifiers,
									accessibility,
									simpleTypeName,
									fullyQualifiedName,
									!CharOperation.equals(fullyQualifiedName, importName[1]),
									scope);
						}

						if (proposeConstructor && !Flags.isEnum(typeModifiers)) {
							boolean isQualified = !CharOperation.equals(fullyQualifiedName, importName[1]);
							if (!isQualified) {
								proposeConstructor(
										simpleTypeName,
										parameterCount,
										signature,
										parameterTypes,
										parameterNames,
										modifiers,
										packageName,
										typeModifiers,
										accessibility,
										simpleTypeName,
										fullyQualifiedName,
										isQualified,
										scope,
										extraFlags);
							} else {
								acceptedConstructor.mustBeQualified = true;
								if (DEFER_QUALIFIED_PROPOSALS) {
									deferredProposals.add(acceptedConstructor);
								} else {
									proposeConstructor(acceptedConstructor, scope);
								}
							}
						}
						continue next;
					}
				}


				if (CharOperation.equals(this.currentPackageName, packageName)) {
					if (proposeType) {
						proposeType(
								packageName,
								simpleTypeName,
								typeModifiers,
								accessibility,
								simpleTypeName,
								fullyQualifiedName,
								false,
								scope);
					}

					if (proposeConstructor && !Flags.isEnum(typeModifiers)) {
						proposeConstructor(
								simpleTypeName,
								parameterCount,
								signature,
								parameterTypes,
								parameterNames,
								modifiers,
								packageName,
								typeModifiers,
								accessibility,
								simpleTypeName,
								fullyQualifiedName,
								false,
								scope,
								extraFlags);
					}
					continue next;
				} else {
					char[] fullyQualifiedEnclosingTypeOrPackageName = null;

					AcceptedConstructor foundConstructor = null;
					if((foundConstructor = (AcceptedConstructor)onDemandFound.get(simpleTypeName)) == null) {
						for (int j = 0; j < this.onDemandImportCacheCount; j++) {
							ImportBinding importBinding = this.onDemandImportsCache[j];

							char[][] importName = importBinding.compoundName;
							char[] importFlatName = CharOperation.concatWith(importName, '.');

							if(fullyQualifiedEnclosingTypeOrPackageName == null) {
								fullyQualifiedEnclosingTypeOrPackageName = packageName;
							}
							if(CharOperation.equals(fullyQualifiedEnclosingTypeOrPackageName, importFlatName)) {
								if(importBinding.isStatic()) {
									if((typeModifiers & ClassFileConstants.AccStatic) != 0) {
										onDemandFound.put(
												simpleTypeName,
												acceptedConstructor);
										continue next;
									}
								} else {
									onDemandFound.put(
											simpleTypeName,
											acceptedConstructor);
									continue next;
								}
							}
						}
					} else if(!foundConstructor.mustBeQualified){
						done : for (int j = 0; j < this.onDemandImportCacheCount; j++) {
							ImportBinding importBinding = this.onDemandImportsCache[j];

							char[][] importName = importBinding.compoundName;
							char[] importFlatName = CharOperation.concatWith(importName, '.');

							if(fullyQualifiedEnclosingTypeOrPackageName == null) {
								fullyQualifiedEnclosingTypeOrPackageName = packageName;
							}
							if(CharOperation.equals(fullyQualifiedEnclosingTypeOrPackageName, importFlatName)) {
								if(importBinding.isStatic()) {
									if((typeModifiers & ClassFileConstants.AccStatic) != 0) {
										foundConstructor.mustBeQualified = true;
										break done;
									}
								} else {
									foundConstructor.mustBeQualified = true;
									break done;
								}
							}
						}
					}
					if (proposeType) {
						proposeType(
								packageName,
								simpleTypeName,
								typeModifiers,
								accessibility,
								simpleTypeName,
								fullyQualifiedName,
								true,
								scope);
					}

					if (proposeConstructor && !Flags.isEnum(typeModifiers)) {
						acceptedConstructor.mustBeQualified = true;
						if (DEFER_QUALIFIED_PROPOSALS) {
							deferredProposals.add(acceptedConstructor);
						} else {
							proposeConstructor(acceptedConstructor, scope);
						}
					}
				}
			}

			char[][] keys = onDemandFound.keyTable;
			Object[] values = onDemandFound.valueTable;
			int max = keys.length;
			for (int i = 0; i < max; i++) {

				// does not check cancellation for every types to avoid performance loss
				if ((i % CHECK_CANCEL_FREQUENCY) == 0) checkCancel();

				if(keys[i] != null) {
					AcceptedConstructor value = (AcceptedConstructor) values[i];
					if(value != null) {
						char[] fullyQualifiedEnclosingTypeOrPackageName = null;
						done : for (int j = 0; j < this.onDemandImportCacheCount; j++) {
							ImportBinding importBinding = this.onDemandImportsCache[j];

							char[][] importName = importBinding.compoundName;
							char[] importFlatName = CharOperation.concatWith(importName, '.');

							if(fullyQualifiedEnclosingTypeOrPackageName == null) {
								fullyQualifiedEnclosingTypeOrPackageName = value.packageName;
							}
							if(CharOperation.equals(fullyQualifiedEnclosingTypeOrPackageName, importFlatName)) {
								if(importBinding.isStatic()) {
									if((value.modifiers & ClassFileConstants.AccStatic) != 0) {
										value.mustBeQualified = true;
										break done;
									}
								} else {
									value.mustBeQualified = true;
									break done;
								}
							}
						}
						if (value.proposeType) {
							proposeType(
									value.packageName,
									value.simpleTypeName,
									value.typeModifiers,
									value.accessibility,
									value.simpleTypeName,
									value.fullyQualifiedName,
									value.mustBeQualified,
									scope);
						}

						if (value.proposeConstructor && !Flags.isEnum(value.modifiers)) {
							if (!value.mustBeQualified) {
								proposeConstructor(
										value.simpleTypeName,
										value.parameterCount,
										value.signature,
										value.parameterTypes,
										value.parameterNames,
										value.modifiers,
										value.packageName,
										value.typeModifiers,
										value.accessibility,
										value.simpleTypeName,
										value.fullyQualifiedName,
										value.mustBeQualified,
										scope,
										value.extraFlags);
							} else {
								if (DEFER_QUALIFIED_PROPOSALS) {
									deferredProposals.add(value);
								} else {
									proposeConstructor(value, scope);
								}
							}
						}
					}
				}
			}

			if (DEFER_QUALIFIED_PROPOSALS) {
				int size = deferredProposals.size();
				for (int i = 0; i < size; i++) {

					// does not check cancellation for every types to avoid performance loss
					if ((i % CHECK_CANCEL_FREQUENCY) == 0) checkCancel();

					AcceptedConstructor deferredProposal = (AcceptedConstructor)deferredProposals.get(i);

					if (deferredProposal.proposeConstructor) {
						proposeConstructor(
								deferredProposal.simpleTypeName,
								deferredProposal.parameterCount,
								deferredProposal.signature,
								deferredProposal.parameterTypes,
								deferredProposal.parameterNames,
								deferredProposal.modifiers,
								deferredProposal.packageName,
								deferredProposal.typeModifiers,
								deferredProposal.accessibility,
								deferredProposal.simpleTypeName,
								deferredProposal.fullyQualifiedName,
								deferredProposal.mustBeQualified,
								scope,
								deferredProposal.extraFlags);
					}
				}
			}
		} finally {
			this.acceptedTypes = null; // reset
		}
	}

	/**
	 * One result of the search consists of a new module.
	 *
	 * NOTE - All module names are presented in their readable form:
	 *    Module names are in the form "a.b.c".
	 *    The default module is represented by an empty array.
	 */
	@Override
	public void acceptModule(char[] moduleName) {
		if (this.knownModules.containsKey(moduleName)) return;
		if (this.assistNodeInJavadoc == 0 && this.moduleDeclaration != null && CharOperation.equals(moduleName, this.moduleDeclaration.moduleName)) return;
		if (CharOperation.equals(moduleName, CharOperation.NO_CHAR)) return;
		this.knownModules.put(moduleName, this);
		char[] completion = moduleName;
		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForCaseMatching(this.qualifiedCompletionToken == null ? this.completionToken : this.qualifiedCompletionToken, moduleName);
		relevance += computeRelevanceForQualification(true);
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
		this.noProposal = false;
		if(!this.requestor.isIgnored(CompletionProposal.MODULE_REF)) {
			InternalCompletionProposal proposal = createProposal(CompletionProposal.MODULE_REF, this.actualCompletionPosition);
			if (this.assistNodeInJavadoc != 0) {
				completion = new char[moduleName.length + 1];
				System.arraycopy(moduleName, 0, completion, 0, moduleName.length);
				completion[moduleName.length] = '/';
			}
			proposal.setModuleName(moduleName);
			proposal.setDeclarationSignature(moduleName);
			proposal.setCompletion(completion);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}

	}

	@Override
	public void acceptPackage(char[] packageName) {
		acceptPackage(packageName, null);
	}

	private void acceptPackage(char[] packageName, char[] moduleName) {

		if (this.knownPkgs.containsKey(packageName)) return;

		if (!isValidPackageName(packageName)) return;

		if (this.skipDefaultPackage &&
			CharOperation.equals(packageName, CharOperation.NO_CHAR))
			return;

		this.knownPkgs.put(packageName, this);

		char[] completion;
		if(this.resolvingImports) {
			if(this.resolvingStaticImports) {
				completion = CharOperation.concat(packageName, new char[] { '.' });
			} else {
				completion = CharOperation.concat(packageName, new char[] { '.', '*', ';' });
			}
		} else {
			completion = packageName;
		}

		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForCaseMatching(this.qualifiedCompletionToken == null ? this.completionToken : this.qualifiedCompletionToken, packageName);
		if(!this.resolvingImports) {
			relevance += computeRelevanceForQualification(true);
		}
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

		this.noProposal = false;
		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			InternalCompletionProposal proposal = createProposal(CompletionProposal.PACKAGE_REF, this.actualCompletionPosition);
			char[] signature = packageName;
			if (moduleName != null) {
				signature = CharOperation.append(packageName, " - ".toCharArray()); //$NON-NLS-1$
				signature = CharOperation.append(signature, moduleName);
				signature = CharOperation.append(signature, '/');
				completion = CharOperation.append(moduleName, '/');
				completion = CharOperation.append(completion, packageName);
			}
			proposal.setDeclarationSignature(signature);
			proposal.setPackageName(packageName);
			proposal.setCompletion(completion);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}

	@Override
	public void acceptType(
		char[] packageName,
		char[] simpleTypeName,
		char[][] enclosingTypeNames,
		int modifiers,
		AccessRestriction accessRestriction) {

		// does not check cancellation for every types to avoid performance loss
		if ((this.foundTypesCount % CHECK_CANCEL_FREQUENCY) == 0) checkCancel();
		this.foundTypesCount++;

		if (this.options.checkDeprecation && (modifiers & ClassFileConstants.AccDeprecated) != 0) return;
		if (this.assistNodeIsExtendedType && (modifiers & ClassFileConstants.AccFinal) != 0) return;
		if (this.assistNodeIsExtendedType && (modifiers & ExtraCompilerModifiers.AccRecord) != 0) return;

		if (this.options.checkVisibility) {
			if((modifiers & ClassFileConstants.AccPublic) == 0) {
				if((modifiers & ClassFileConstants.AccPrivate) != 0) return;
				if (this.moduleDeclaration == null) {
					char[] currentPackage = CharOperation.concatWith(this.unitScope.fPackage.compoundName, '.');
					if(!CharOperation.equals(packageName, currentPackage)) return;
				}
			}
		}

		int accessibility = IAccessRule.K_ACCESSIBLE;
		if(accessRestriction != null) {
			switch (accessRestriction.getProblemId()) {
				case IProblem.ForbiddenReference:
					if (this.options.checkForbiddenReference) {
						return;
					}
					accessibility = IAccessRule.K_NON_ACCESSIBLE;
					break;
				case IProblem.DiscouragedReference:
					if (this.options.checkDiscouragedReference) {
						return;
					}
					accessibility = IAccessRule.K_DISCOURAGED;
					break;
			}
		}

		if (isForbidden(packageName, simpleTypeName, enclosingTypeNames)) {
			return;
		}

		if(this.acceptedTypes == null) {
			this.acceptedTypes = new ObjectVector();
		}
		this.acceptedTypes.add(new AcceptedType(packageName, simpleTypeName, enclosingTypeNames, modifiers, accessibility));
	}

	private void acceptTypes(Scope scope) {
		this.checkCancel();

		if(this.acceptedTypes == null) return;

		int length = this.acceptedTypes.size();

		if(length == 0) return;

		HashtableOfObject onDemandFound = new HashtableOfObject();

		try {
			next : for (int i = 0; i < length; i++) {

				// does not check cancellation for every types to avoid performance loss
				if ((i % CHECK_CANCEL_FREQUENCY) == 0) checkCancel();

				AcceptedType acceptedType = (AcceptedType)this.acceptedTypes.elementAt(i);
				char[] packageName = acceptedType.packageName;
				char[] simpleTypeName = acceptedType.simpleTypeName;
				char[][] enclosingTypeNames = acceptedType.enclosingTypeNames;
				int modifiers = acceptedType.modifiers;
				int accessibility = acceptedType.accessibility;

				char[] typeName;
				char[] flatEnclosingTypeNames;
				if(enclosingTypeNames == null || enclosingTypeNames.length == 0) {
					flatEnclosingTypeNames = null;
					typeName = simpleTypeName;
				} else {
					flatEnclosingTypeNames = CharOperation.concatWith(acceptedType.enclosingTypeNames, '.');
					typeName = CharOperation.concat(flatEnclosingTypeNames, simpleTypeName, '.');
				}
				char[] fullyQualifiedName = CharOperation.concat(packageName, typeName, '.');

				if (this.knownTypes.containsKey(fullyQualifiedName)) continue next;

				this.knownTypes.put(fullyQualifiedName, KNOWN_TYPE_WITH_UNKNOWN_CONSTRUCTORS);

				if (this.resolvingImports) {
					if(this.compilerOptions.complianceLevel >= ClassFileConstants.JDK1_4 && packageName.length == 0) {
						continue next; // import of default package is forbidden when compliance is 1.4 or higher
					}

					char[] completionName = this.insideQualifiedReference ? simpleTypeName : fullyQualifiedName;

					if(this.resolvingStaticImports) {
						if(enclosingTypeNames == null || enclosingTypeNames.length == 0) {
							completionName = CharOperation.concat(completionName, new char[] { '.' });
						} else if ((modifiers & ClassFileConstants.AccStatic) == 0) {
							continue next;
						} else {
							completionName = appendUnlessNextToken(completionName, new char[] { ';' }, TerminalTokens.TokenNameSEMICOLON);
						}
					} else {
						completionName = appendUnlessNextToken(completionName, new char[] {';'}, TerminalTokens.TokenNameSEMICOLON);
					}

					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForResolution();
					relevance += computeRelevanceForInterestingProposal(packageName, fullyQualifiedName);
					relevance += computeRelevanceForRestrictions(accessibility);
					relevance += computeRelevanceForCaseMatching(this.completionToken, simpleTypeName);

					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
						createTypeProposal(packageName, typeName, modifiers, accessibility, completionName, relevance);
					}
				} else {
					if(!this.importCachesInitialized) {
						initializeImportCaches();
					}

					for (int j = 0; j < this.importCacheCount; j++) {
						char[][] importName = this.importsCache[j];
						if(CharOperation.equals(typeName, importName[0])) {
							proposeType(
									packageName,
									simpleTypeName,
									modifiers,
									accessibility,
									typeName,
									fullyQualifiedName,
									!CharOperation.equals(fullyQualifiedName, importName[1]),
									scope);
							continue next;
						}
					}


					if ((enclosingTypeNames == null || enclosingTypeNames.length == 0 ) && CharOperation.equals(this.currentPackageName, packageName)) {
						proposeType(
								packageName,
								simpleTypeName,
								modifiers,
								accessibility,
								typeName,
								fullyQualifiedName,
								false,
								scope);
						continue next;
					} else {
						char[] fullyQualifiedEnclosingTypeOrPackageName = null;

						AcceptedType foundType = null;
						if((foundType = (AcceptedType)onDemandFound.get(simpleTypeName)) == null) {
							for (int j = 0; j < this.onDemandImportCacheCount; j++) {
								ImportBinding importBinding = this.onDemandImportsCache[j];

								char[][] importName = importBinding.compoundName;
								char[] importFlatName = CharOperation.concatWith(importName, '.');

								if(fullyQualifiedEnclosingTypeOrPackageName == null) {
									if(enclosingTypeNames != null && enclosingTypeNames.length != 0) {
										fullyQualifiedEnclosingTypeOrPackageName =
											CharOperation.concat(
													packageName,
													flatEnclosingTypeNames,
													'.');
									} else {
										fullyQualifiedEnclosingTypeOrPackageName =
											packageName;
									}
								}
								if(CharOperation.equals(fullyQualifiedEnclosingTypeOrPackageName, importFlatName)) {
									if(importBinding.isStatic()) {
										if((modifiers & ClassFileConstants.AccStatic) != 0) {
											acceptedType.qualifiedTypeName = typeName;
											acceptedType.fullyQualifiedName = fullyQualifiedName;
											onDemandFound.put(
													simpleTypeName,
													acceptedType);
											continue next;
										}
									} else {
										acceptedType.qualifiedTypeName = typeName;
										acceptedType.fullyQualifiedName = fullyQualifiedName;
										onDemandFound.put(
												simpleTypeName,
												acceptedType);
										continue next;
									}
								}
							}
						} else if(!foundType.mustBeQualified){
							done : for (int j = 0; j < this.onDemandImportCacheCount; j++) {
								ImportBinding importBinding = this.onDemandImportsCache[j];

								char[][] importName = importBinding.compoundName;
								char[] importFlatName = CharOperation.concatWith(importName, '.');

								if(fullyQualifiedEnclosingTypeOrPackageName == null) {
									if(enclosingTypeNames != null && enclosingTypeNames.length != 0) {
										fullyQualifiedEnclosingTypeOrPackageName =
											CharOperation.concat(
													packageName,
													flatEnclosingTypeNames,
													'.');
									} else {
										fullyQualifiedEnclosingTypeOrPackageName =
											packageName;
									}
								}
								if(CharOperation.equals(fullyQualifiedEnclosingTypeOrPackageName, importFlatName)) {
									if(importBinding.isStatic()) {
										if((modifiers & ClassFileConstants.AccStatic) != 0) {
											foundType.mustBeQualified = true;
											break done;
										}
									} else {
										foundType.mustBeQualified = true;
										break done;
									}
								}
							}
						}
						proposeType(
								packageName,
								simpleTypeName,
								modifiers,
								accessibility,
								typeName,
								fullyQualifiedName,
								true,
								scope);
					}
				}
			}

			char[][] keys = onDemandFound.keyTable;
			Object[] values = onDemandFound.valueTable;
			int max = keys.length;
			for (int i = 0; i < max; i++) {
				if ((i % CHECK_CANCEL_FREQUENCY) == 0) checkCancel();
				if(keys[i] != null) {
					AcceptedType value = (AcceptedType) values[i];
					if(value != null) {
						proposeType(
								value.packageName,
								value.simpleTypeName,
								value.modifiers,
								value.accessibility,
								value.qualifiedTypeName,
								value.fullyQualifiedName,
								value.mustBeQualified,
								scope);
					}
				}
			}
		} finally {
			this.acceptedTypes = null; // reset
		}
	}

	private char[] appendUnlessNextToken(char[] completionName, char[] suffix, int nextToken) {
		if (this.source == null)
			return CharOperation.concat(completionName, suffix);

		AssistParser assistParser = getParser();
		Object parserState = assistParser.becomeSimpleParser();

		assistParser.scanner.setSource(this.source);
		assistParser.scanner.resetTo(this.endPosition, Integer.MAX_VALUE);
		try {
			if (assistParser.scanner.getNextToken() != nextToken) {
				return CharOperation.concat(completionName, suffix);
			}
		} catch (InvalidInputException e) {
			// ignore
		} finally {
			assistParser.restoreAssistParser(parserState);
		}
		return completionName;
	}

	public void acceptUnresolvedName(char[] name) {
		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution(false);
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForCaseMatching(this.completionToken, name);
		relevance += computeRelevanceForQualification(false);
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for local variable
		CompletionEngine.this.noProposal = false;
		if(!CompletionEngine.this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
			InternalCompletionProposal proposal = CompletionEngine.this.createProposal(CompletionProposal.LOCAL_VARIABLE_REF, CompletionEngine.this.actualCompletionPosition);
			proposal.setSignature(JAVA_LANG_OBJECT_SIGNATURE);
			proposal.setPackageName(JAVA_LANG_NAME);
			proposal.setTypeName(OBJECT);
			proposal.setName(name);
			proposal.setCompletion(name);
			proposal.setFlags(Flags.AccDefault);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			CompletionEngine.this.requestor.accept(proposal);
			if(DEBUG) {
				CompletionEngine.this.printDebug(proposal);
			}
		}
	}
	private void addExpectedType(TypeBinding type, Scope scope){
		if (type == null || !type.isValidBinding() || type == TypeBinding.NULL || type == TypeBinding.VOID) return;

		// do not add twice the same type
		for (int i = 0; i <= this.expectedTypesPtr; i++) {
			if (TypeBinding.equalsEquals(this.expectedTypes[i], type)) return;
		}

		int length = this.expectedTypes.length;
		if (++this.expectedTypesPtr >= length)
			System.arraycopy(this.expectedTypes, 0, this.expectedTypes = new TypeBinding[length * 2], 0, length);
		this.expectedTypes[this.expectedTypesPtr] = type;

		if(TypeBinding.equalsEquals(type, scope.getJavaLangObject())) {
			this.hasJavaLangObjectAsExpectedType = true;
		}
	}

	private void addForbiddenBindings(Binding binding){
		if (binding == null) return;

		int length = this.forbbidenBindings.length;
		if (++this.forbbidenBindingsPtr >= length)
			System.arraycopy(this.forbbidenBindings, 0, this.forbbidenBindings = new Binding[length * 2], 0, length);
		this.forbbidenBindings[this.forbbidenBindingsPtr] = binding;
	}

	private void addUninterestingBindings(Binding binding){
		if (binding == null) return;

		int length = this.uninterestingBindings.length;
		if (++this.uninterestingBindingsPtr >= length)
			System.arraycopy(this.uninterestingBindings, 0, this.uninterestingBindings = new Binding[length * 2], 0, length);
		this.uninterestingBindings[this.uninterestingBindingsPtr] = binding;
	}

	// this code is derived from MethodBinding#areParametersCompatibleWith(TypeBinding[])
	private final boolean areParametersCompatibleWith(TypeBinding[] parameters, TypeBinding[] arguments, boolean isVarargs) {
		int paramLength = parameters.length;
		int argLength = arguments.length;
		int lastIndex = argLength;
		if (isVarargs) {
			lastIndex = paramLength - 1;
			if (paramLength == argLength) { // accept X[] but not X or X[][]
				TypeBinding varArgType = parameters[lastIndex]; // is an ArrayBinding by definition
				TypeBinding lastArgument = arguments[lastIndex];
				if (TypeBinding.notEquals(varArgType, lastArgument) && !lastArgument.isCompatibleWith(varArgType))
					return false;
			} else if (paramLength < argLength) { // all remainig argument types must be compatible with the elementsType of varArgType
				TypeBinding varArgType = ((ArrayBinding) parameters[lastIndex]).elementsType();
				for (int i = lastIndex; i < argLength; i++)
					if (TypeBinding.notEquals(varArgType, arguments[i]) && !arguments[i].isCompatibleWith(varArgType))
						return false;
			} else if (lastIndex != argLength) { // can call foo(int i, X ... x) with foo(1) but NOT foo();
				return false;
			}
			// now compare standard arguments from 0 to lastIndex
		} else {
			if(paramLength != argLength)
				return false;
		}
		for (int i = 0; i < lastIndex; i++)
			if (TypeBinding.notEquals(parameters[i], arguments[i]) && !arguments[i].isCompatibleWith(parameters[i]))
				return false;
		return true;
	}

	private void buildContext(
			ASTNode astNode,
			ASTNode astNodeParent,
			CompilationUnitDeclaration compilationUnitDeclaration,
			Binding qualifiedBinding,
			Scope scope) {
		InternalCompletionContext context = new InternalCompletionContext();
		if (this.requestor.isExtendedContextRequired()) {
			context.setExtendedData(
					this.typeRoot,
					compilationUnitDeclaration,
					this.lookupEnvironment,
					scope,
					astNode,
					astNodeParent,
					this.owner,
					this.parser);
		}

		// build expected types context
		if (this.expectedTypesPtr > -1) {
			int length = this.expectedTypesPtr + 1;
			char[][] expTypes = new char[length][];
			char[][] expKeys = new char[length][];
			for (int i = 0; i < length; i++) {
				expTypes[i] = getSignature(this.expectedTypes[i]);
				expKeys[i] = this.expectedTypes[i].computeUniqueKey();
			}
			context.setExpectedTypesSignatures(expTypes);
			context.setExpectedTypesKeys(expKeys);
		}

		context.setOffset(this.actualCompletionPosition + 1 - this.offset);

		// Set javadoc info
		if (astNode instanceof CompletionOnJavadoc) {
			this.assistNodeInJavadoc = ((CompletionOnJavadoc)astNode).getCompletionFlags();
			context.setJavadoc(this.assistNodeInJavadoc);
		}

		if (!(astNode instanceof CompletionOnJavadoc)) {
			CompletionScanner scanner = (CompletionScanner)this.parser.scanner;
			context.setToken(scanner.completionIdentifier);
			context.setTokenRange(
					scanner.completedIdentifierStart - this.offset,
					scanner.completedIdentifierEnd - this.offset,
					scanner.endOfEmptyToken - this.offset);
		} else if(astNode instanceof CompletionOnJavadocTag) {
			CompletionOnJavadocTag javadocTag = (CompletionOnJavadocTag) astNode;
			context.setToken(CharOperation.concat(new char[]{'@'}, javadocTag.token));
			context.setTokenRange(
					javadocTag.tagSourceStart - this.offset,
					javadocTag.tagSourceEnd - this.offset,
					((CompletionScanner)this.parser.javadocParser.scanner).endOfEmptyToken - this.offset);
		} else {
			CompletionScanner scanner = (CompletionScanner)this.parser.javadocParser.scanner;
			context.setToken(scanner.completionIdentifier);
			context.setTokenRange(
					scanner.completedIdentifierStart - this.offset,
					scanner.completedIdentifierEnd - this.offset,
					scanner.endOfEmptyToken - this.offset);
		}

		if(astNode instanceof CompletionOnStringLiteral) {
			context.setTokenKind(CompletionContext.TOKEN_KIND_STRING_LITERAL);
		} else {
			context.setTokenKind(CompletionContext.TOKEN_KIND_NAME);
		}

		buildTokenLocationContext(context, scope, astNode, astNodeParent);

		if(DEBUG) {
			trace(context.toString());
		}
		this.requestor.acceptContext(context);
	}

	private void buildTokenLocationContext(InternalCompletionContext context, Scope scope, ASTNode astNode, ASTNode astNodeParent) {
		if (scope == null || context.isInJavadoc()) return;

		if (astNode instanceof CompletionOnFieldType) {
			CompletionOnFieldType field = (CompletionOnFieldType) astNode;
			if (!field.isLocalVariable &&
					field.modifiers == ClassFileConstants.AccDefault &&
					(field.annotations == null || field.annotations.length == 0)) {
				context.setTokenLocation(CompletionContext.TL_MEMBER_START);
			}
		} else if (astNode instanceof CompletionOnMethodReturnType) {
			CompletionOnMethodReturnType method = (CompletionOnMethodReturnType) astNode;
			if (method.modifiers == ClassFileConstants.AccDefault &&
					(method.annotations == null || method.annotations.length == 0)) {
				context.setTokenLocation(CompletionContext.TL_MEMBER_START);
			}
		} else if (astNode instanceof CompletionOnSingleTypeReference) {
			CompletionOnSingleTypeReference completionOnSingleTypeReference = (CompletionOnSingleTypeReference) astNode;
			if (completionOnSingleTypeReference.isConstructorType) {
				context.setTokenLocation(CompletionContext.TL_CONSTRUCTOR_START);
			} else if (astNodeParent instanceof LambdaExpression || astNodeParent instanceof LocalDeclaration) {
				context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
			}
		} else if (astNode instanceof CompletionOnQualifiedTypeReference) {
			CompletionOnQualifiedTypeReference completionOnQualifiedTypeReference = (CompletionOnQualifiedTypeReference) astNode;
			if (completionOnQualifiedTypeReference.isConstructorType){
				context.setTokenLocation(CompletionContext.TL_CONSTRUCTOR_START);
			} else if (astNodeParent instanceof LambdaExpression) {
				context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
			}
		} else if (astNode instanceof CompletionOnKeyword3 && ((CompletionOnKeyword3) astNode).afterTryOrCatch()) {
				context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
		} else if (astNode instanceof CompletionOnImportReference) {
			context.setTokenLocation(CompletionContext.TL_IN_IMPORT);
		} else {
			ReferenceContext referenceContext = scope.referenceContext();
			if (referenceContext instanceof AbstractMethodDeclaration) {
				AbstractMethodDeclaration methodDeclaration = (AbstractMethodDeclaration)referenceContext;
				if (methodDeclaration.bodyStart <= astNode.sourceStart &&
						astNode.sourceEnd <= methodDeclaration.bodyEnd) {
					// completion is inside a method body
					if (astNodeParent == null &&
							astNode instanceof CompletionOnSingleNameReference &&
							!((CompletionOnSingleNameReference)astNode).isPrecededByModifiers) {
						context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
					}
				}
			} else if (referenceContext instanceof LambdaExpression) {
				LambdaExpression expression = (LambdaExpression)referenceContext;
				if (expression.body().sourceStart <= astNode.sourceStart &&
						astNode.sourceEnd <= expression.body().sourceEnd) {
					// completion is inside a method body
					if ((astNodeParent == null || astNodeParent == expression || (astNodeParent instanceof Block)) &&
							astNode instanceof CompletionOnSingleNameReference &&
							!((CompletionOnSingleNameReference)astNode).isPrecededByModifiers) {
						context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
					}
				}
			} else if (referenceContext instanceof TypeDeclaration) {
				TypeDeclaration typeDeclaration = (TypeDeclaration) referenceContext;
				FieldDeclaration[] fields = typeDeclaration.fields;
				if (fields != null) {
					done : for (int i = 0; i < fields.length; i++) {
						if (fields[i] instanceof Initializer) {
							Initializer initializer = (Initializer) fields[i];
							if (initializer.block.sourceStart <= astNode.sourceStart &&
									astNode.sourceStart < initializer.bodyEnd) {
								// completion is inside an initializer
								if (astNodeParent == null &&
										astNode instanceof CompletionOnSingleNameReference &&
										!((CompletionOnSingleNameReference)astNode).isPrecededByModifiers) {
									context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
								}
								break done;
							}
						}
					}
				}
			}
		}
	}

	void checkCancel() {
		if (this.monitor != null && this.monitor.isCanceled()) {
			throw new OperationCanceledException();
		}
	}

	private boolean complete(
			ASTNode astNode,
			ASTNode astNodeParent,
			ASTNode enclosingNode,
			CompilationUnitDeclaration compilationUnitDeclaration,
			Binding qualifiedBinding,
			Scope scope,
			boolean insideTypeAnnotation) {

		setSourceAndTokenRange(astNode.sourceStart, astNode.sourceEnd);

		scope = computeForbiddenBindings(astNode, astNodeParent, scope);
		computeUninterestingBindings(astNode, astNodeParent, scope);
		if(astNodeParent != null) {
			if(!isValidParent(astNodeParent, astNode, scope)) return false;
			computeExpectedTypes(astNodeParent, astNode, scope);
		}

		buildContext(astNode, astNodeParent, compilationUnitDeclaration, qualifiedBinding, scope);

		if (astNode instanceof CompletionOnMemberAccess && qualifiedBinding instanceof BaseTypeBinding) return true;
		if (astNode instanceof CompletionOnFieldType) {
			completionOnFieldType(astNode, scope);
		} else if (astNode instanceof CompletionOnMethodReturnType) {
			completionOnMethodReturnType(astNode, scope);
		} else if (astNode instanceof CompletionOnSingleNameReference) {
			completionOnSingleNameReference(astNode, astNodeParent, scope, insideTypeAnnotation);
		} else if (astNode instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
			completionOnProvidesInterfacesQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnProvidesInterfacesSingleTypeReference) {
			completionOnProvidesInterfacesSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
			completionOnProvidesImplementationsQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnProvidesImplementationsSingleTypeReference) {
			completionOnProvidesImplementationsSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnSingleTypeReference) {
			completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnQualifiedNameReference) {
			completionOnQualifiedNameReference(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation);
		} else if (astNode instanceof CompletionOnQualifiedTypeReference) {
			completionOnQualifiedTypeReference(astNode, astNodeParent, enclosingNode, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnMemberAccess) {
			completionOnMemberAccess(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation);
		} else if (astNode instanceof CompletionOnMessageSend) {
			completionOnMessageSend(astNode, qualifiedBinding, scope);
			// rebuild the context with newly found expected types so other completion computers can benifit from it.
			if(this.expectedTypesPtr > -1) {
				buildContext(astNode, astNodeParent, compilationUnitDeclaration, qualifiedBinding, scope);
			}
		} else if (astNode instanceof CompletionOnExplicitConstructorCall) {
			completionOnExplicitConstructorCall(astNode, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnQualifiedAllocationExpression) {
			completionOnQualifiedAllocationExpression(astNode, qualifiedBinding, scope);
			// rebuild the context with newly found expected types so other completion computers can benefit from it.
			if(this.expectedTypesPtr > -1) {
				buildContext(astNode, astNodeParent, compilationUnitDeclaration, qualifiedBinding, scope);
			}
		} else if (astNode instanceof CompletionOnClassLiteralAccess) {
			completionOnClassLiteralAccess(astNode, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnMethodName) {
			completionOnMethodName(astNode, scope);
		} else if (astNode instanceof CompletionOnFieldName) {
			completionOnFieldName(astNode, scope);
		} else if (astNode instanceof CompletionOnRecordComponentName) {
			completionOnRecordComponentName(astNode, scope);
		} else if (astNode instanceof CompletionOnLocalName) {
			completionOnLocalOrArgumentName(astNode, scope);
		} else if (astNode instanceof CompletionOnArgumentName) {
			completionOnLocalOrArgumentName(astNode, scope);
		} else if (astNode instanceof CompletionOnKeyword) {
			completionOnKeyword(astNode);
		} else if (astNode instanceof CompletionOnParameterizedQualifiedTypeReference) {
			completionOnParameterizedQualifiedTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnMarkerAnnotationName) {
			completionOnMarkerAnnotationName(astNode, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnMemberValueName) {
			completionOnMemberValueName(astNode, astNodeParent, scope, insideTypeAnnotation);
		} else if(astNode instanceof CompletionOnBreakStatement) {
			completionOnBreakStatement((CompletionOnBreakStatement) astNode);
		} else if(astNode instanceof CompletionOnContinueStatement) {
			completionOnContinueStatement((CompletionOnContinueStatement) astNode);
		} else if(astNode instanceof CompletionOnMessageSendName) {
			completionOnMessageSendName(astNode, qualifiedBinding, scope);
		} else if (astNode instanceof CompletionOnReferenceExpressionName) {
			completionOnReferenceExpressionName(astNode, qualifiedBinding, scope);
		// Completion on Javadoc nodes
		} else if ((astNode.bits & ASTNode.InsideJavadoc) != 0) {
			if (astNode instanceof CompletionOnJavadocSingleTypeReference) {
				completionOnJavadocSingleTypeReference(astNode, scope);
			} else if (astNode instanceof CompletionOnJavadocQualifiedTypeReference) {
				completionOnJavadocQualifiedTypeReference(astNode, qualifiedBinding, scope);
			} else if (astNode instanceof CompletionOnJavadocModuleReference) {
				completionOnJavadocModuleReference(astNode, qualifiedBinding, scope);
			} else if (astNode instanceof CompletionOnJavadocFieldReference) {
				completionOnJavadocFieldReference(astNode, scope);
			} else if (astNode instanceof CompletionOnJavadocMessageSend) {
				completionOnJavadocMessageSend(astNode, qualifiedBinding, scope);
			} else if (astNode instanceof CompletionOnJavadocAllocationExpression) {
				completionOnJavadocAllocationExpression(astNode, qualifiedBinding, scope);
			} else if (astNode instanceof CompletionOnJavadocParamNameReference) {
				completionOnJavadocParamNameReference(astNode);
			} else if (astNode instanceof CompletionOnJavadocTypeParamReference) {
				completionOnJavadocTypeParamReference(astNode);
			} else if (astNode instanceof CompletionOnJavadocTag) {
				completionOnJavadocTag(astNode);
			}
		}
		return true;
	}

	/**
	 * Ask the engine to compute a completion at the specified position
	 * of the given compilation unit.
	 *
	 *  No return
	 *      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 pos, ITypeRoot root) {

		if(DEBUG) {
			trace("COMPLETION IN " + new String(sourceUnit.getFileName()) + " AT POSITION " + completionPosition);  //$NON-NLS-1$//$NON-NLS-2$
			trace("COMPLETION - Source :" + new String(sourceUnit.getContents())); //$NON-NLS-1$
		}
		if (this.monitor != null) this.monitor.beginTask(Messages.engine_completing, IProgressMonitor.UNKNOWN);
		this.requestor.beginReporting();
		boolean contextAccepted = false;
		try {
			this.fileName = sourceUnit.getFileName();
			this.actualCompletionPosition = completionPosition - 1;
			this.offset = pos;
			this.typeRoot = root;
			this.source = sourceUnit.getContents();

			this.checkCancel();

			// for now until we can change the UI.
			CompilationResult result = new CompilationResult(sourceUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
			CompilationUnitDeclaration parsedUnit = this.parser.dietParse(sourceUnit, result, this.actualCompletionPosition);

			//		boolean completionNodeFound = false;
			if (parsedUnit != null) {
				if(DEBUG) {
					trace("COMPLETION - Diet AST :"); //$NON-NLS-1$
					trace(parsedUnit.toString());
				}

				if (parsedUnit.isModuleInfo()) {
					this.moduleDeclaration = parsedUnit.moduleDeclaration;
					if (this.moduleDeclaration == null) return;
					if (this.moduleDeclaration instanceof CompletionOnModuleDeclaration) {
						contextAccepted = true;
						buildContext(parsedUnit.moduleDeclaration, null, parsedUnit, null, null);
						//this.requestor.setIgnored(CompletionProposal.MODULE_DECLARATION, false); //TODO: Hack until ui fixes this issue.
						if(!this.requestor.isIgnored(CompletionProposal.MODULE_DECLARATION)) {
							proposeModuleName(parsedUnit);
						}
						debugPrintf();
						return;
					}
					if (this.moduleDeclaration instanceof CompletionOnKeywordModuleDeclaration) {
						contextAccepted = true;
						processModuleKeywordCompletion(parsedUnit, this.moduleDeclaration, (CompletionOnKeyword) this.moduleDeclaration);
						return;
					}
					if (this.moduleDeclaration.exports != null) {
						contextAccepted = completeOnPackageVisibilityStatements(contextAccepted, parsedUnit, this.moduleDeclaration.exports);
						if (contextAccepted) return;
					}
					if (this.moduleDeclaration.opens != null) {
						contextAccepted = completeOnPackageVisibilityStatements(contextAccepted, parsedUnit, this.moduleDeclaration.opens);
						if (contextAccepted) return;
					}
					RequiresStatement[] moduleRefs = this.moduleDeclaration.requires;
					if (moduleRefs != null) {
						for (int i = 0, l = moduleRefs.length; i < l; ++i) {
							ModuleReference reference = moduleRefs[i].module;
							if (reference instanceof CompletionOnModuleReference) {
								contextAccepted = true;
								buildContext(reference, null, parsedUnit, null, null);
								if(!this.requestor.isIgnored(CompletionProposal.MODULE_REF)) {
									findModules((CompletionOnModuleReference) reference, false /* targetted */);
								}
								debugPrintf();
								return;
							}
						}
					}
					try {
						UsesStatement[] uses = this.moduleDeclaration.uses;
						if (uses != null) {
							for (int i = 0, l = uses.length; i < l; ++i) {
								UsesStatement usesStatement = uses[i];
								TypeReference usesReference = usesStatement.serviceInterface;
								if (usesReference instanceof CompletionOnUsesSingleTypeReference ||
										usesReference instanceof CompletionOnUsesQualifiedTypeReference) {
									contextAccepted = checkForCNF(usesReference, parsedUnit, true);
									return;
								}
							}
						}
						ProvidesStatement[] providesStmts = this.moduleDeclaration.services;
						for (int i = 0, l = providesStmts != null ? providesStmts.length : 0; i < l; ++i) {
							ProvidesStatement providesStmt = providesStmts[i];
							this.parser.enclosingNode = providesStmt;
							TypeReference pInterface = providesStmt.serviceInterface;
							if (pInterface instanceof CompletionOnProvidesInterfacesSingleTypeReference ||
									pInterface instanceof CompletionOnProvidesInterfacesQualifiedTypeReference) {
								contextAccepted = checkForCNF(pInterface, parsedUnit, true);
								return;
							}
							TypeReference[] implementations = providesStmt.implementations;
							for (int j = 0, k = implementations.length; j < k; ++j) {
								TypeReference implementation = implementations[j];
								if (implementation instanceof CompletionOnProvidesImplementationsSingleTypeReference ||
										implementation instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
									this.skipDefaultPackage = true;
									contextAccepted = checkForCNF(implementation, parsedUnit, false);
									return;
								} else if (implementation instanceof CompletionOnKeyword) {
									contextAccepted = true;
									processModuleKeywordCompletion(parsedUnit, implementation, (CompletionOnKeyword) implementation);
								}
							}
						}
					} catch (CompletionNodeFound e) {
						//					completionNodeFound = true;
						if (e.astNode != null) {
							// if null then we found a problem in the completion node
							if(DEBUG) {
								trace("COMPLETION - Completion node : " + e.astNode.toString()); //$NON-NLS-1$
								if(this.parser.assistNodeParent != null) {
									trace("COMPLETION - Parent Node : " + this.parser.assistNodeParent); //$NON-NLS-1$
								}
							}
							this.lookupEnvironment.unitBeingCompleted = parsedUnit; // better resilient to further error reporting
							contextAccepted =
									complete(
											e.astNode,
											this.parser.assistNodeParent,
											this.parser.enclosingNode,
											parsedUnit,
											e.qualifiedBinding,
											e.scope,
											e.insideTypeAnnotation);
						}
					} finally {
						this.skipDefaultPackage = false;
					}
				}
				// scan the package & import statements first
				if (parsedUnit.currentPackage instanceof CompletionOnPackageReference) {
					contextAccepted = true;
					buildContext(parsedUnit.currentPackage, null, parsedUnit, null, null);
					if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
						findPackages((CompletionOnPackageReference) parsedUnit.currentPackage);
					}
					debugPrintf();
					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) {
							this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
							if ((this.unitScope = parsedUnit.scope) != null) {
								contextAccepted = true;
								buildContext(importReference, null, parsedUnit, null, this.unitScope);

								long positions = importReference.sourcePositions[importReference.tokens.length - 1];
								setSourceAndTokenRange((int) (positions >>> 32), (int) positions);

								char[][] oldTokens = importReference.tokens;
								int tokenCount = oldTokens.length;
								if (tokenCount == 1) {
									findImports((CompletionOnImportReference)importReference, true);
								} else if(tokenCount > 1){
									this.insideQualifiedReference = true;

									char[] lastToken = oldTokens[tokenCount - 1];
									char[][] qualifierTokens = CharOperation.subarray(oldTokens, 0, tokenCount - 1);

									Binding binding = this.unitScope.getTypeOrPackage(qualifierTokens);
									if(binding != null) {
										if(binding instanceof PackageBinding) {
											findImports((CompletionOnImportReference)importReference, false);
										} else {
											ReferenceBinding ref = (ReferenceBinding) binding;

											if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
												findImportsOfMemberTypes(lastToken, ref, importReference.isStatic());
											}
											if(importReference.isStatic()) {

												if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
													findImportsOfStaticFields(lastToken, ref);
												}
												if(!this.requestor.isIgnored(CompletionProposal.METHOD_NAME_REFERENCE)) {
													findImportsOfStaticMethods(lastToken, ref);
												}
											}
										}
									}
								}

								debugPrintf();
							}
							return;
						} else if(importReference instanceof CompletionOnKeyword) {
							contextAccepted = true;
							buildContext(importReference, null, parsedUnit, null, null);
							if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
								setSourceAndTokenRange(importReference.sourceStart, importReference.sourceEnd);
								CompletionOnKeyword keyword = (CompletionOnKeyword)importReference;
								findKeywords(keyword.getToken(), keyword.getPossibleKeywords(), false, parsedUnit.currentPackage != null);
							}
							debugPrintf();
							return;
						}
					}
				}
				// javadoc tag completion in module-info file
				contextAccepted = completeJavadocTagInModuleInfo(parsedUnit);
				if (parsedUnit.types != null) {
					try {
						this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);

						if ((this.unitScope = parsedUnit.scope) != null) {
							this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
							this.unitScope.throwDeferredException();
							parsedUnit.scope.faultInTypes();
							this.unitScope.throwDeferredException();
							parseBlockStatements(parsedUnit, this.actualCompletionPosition);
							if(DEBUG) {
								trace("COMPLETION - AST :"); //$NON-NLS-1$
								trace(parsedUnit.toString());
							}
							parsedUnit.resolve();
						}
					} catch (CompletionNodeFound e) {
						//					completionNodeFound = true;
						if (e.astNode != null) {
							// if null then we found a problem in the completion node
							if(DEBUG) {
								trace("COMPLETION - Completion node : " + e.astNode.toString()); //$NON-NLS-1$
								if(this.parser.assistNodeParent != null) {
									trace("COMPLETION - Parent Node : " + this.parser.assistNodeParent); //$NON-NLS-1$
								}
							}
							this.lookupEnvironment.unitBeingCompleted = parsedUnit; // better resilient to further error reporting
							contextAccepted =
								complete(
									e.astNode,
									this.parser.assistNodeParent,
									this.parser.enclosingNode,
									parsedUnit,
									e.qualifiedBinding,
									e.scope,
									e.insideTypeAnnotation);
						}
					}
				}
			}

			if(this.noProposal && this.problem != null) {
				if(!contextAccepted) {
					contextAccepted = true;
					InternalCompletionContext context = new InternalCompletionContext();
					context.setOffset(completionPosition - this.offset);
					context.setTokenKind(CompletionContext.TOKEN_KIND_UNKNOWN);
					if (this.requestor.isExtendedContextRequired()) context.setExtended();
					this.requestor.acceptContext(context);
				}
				this.requestor.completionFailure(this.problem);
				if(DEBUG) {
					this.printDebug(this.problem);
				}
			}
			/* 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) this.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 | InvalidCursorLocation | AbortCompilation | CompletionNodeFound e){ // internal failure - bugs 5618
			if(DEBUG) {
				trace("Exception caught by CompletionEngine:", e); //$NON-NLS-1$
			}
		} finally {
			if(!contextAccepted) {
				contextAccepted = true;
				InternalCompletionContext context = new InternalCompletionContext();
				context.setTokenKind(CompletionContext.TOKEN_KIND_UNKNOWN);
				context.setOffset(completionPosition - this.offset);
				if (this.requestor.isExtendedContextRequired()) context.setExtended();
				this.requestor.acceptContext(context);
			}
			this.requestor.endReporting();
			if (this.monitor != null) this.monitor.done();
			reset();
		}
	}

	private boolean completeJavadocTagInModuleInfo(CompilationUnitDeclaration parsedUnit) {
		boolean contextAccepted = false;
		if (this.parser.assistNodeParent instanceof CompletionJavadoc && parsedUnit.isModuleInfo() ) {
			try {
				this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
				if(this.parser.assistNode instanceof CompletionOnJavadocTag) {
					((CompletionOnJavadocTag)this.parser.assistNode).filterPossibleTags(parsedUnit.scope);
				}
				throw new CompletionNodeFound(this.parser.assistNode, null, parsedUnit.scope);
			}
			catch (CompletionNodeFound e) {
				this.unitScope = parsedUnit.scope;
				if (e.astNode != null) {
					// if null then we found a problem in the completion node
					if(DEBUG) {
						trace("COMPLETION - Completion node : " + e.astNode.toString()); //$NON-NLS-1$
						if(this.parser.assistNodeParent != null) {
							trace("COMPLETION - Parent Node : " + this.parser.assistNodeParent); //$NON-NLS-1$
						}
					}
					this.lookupEnvironment.unitBeingCompleted = parsedUnit; // better resilient to further error reporting
					contextAccepted =
						complete(
							e.astNode,
							this.parser.assistNodeParent,
							this.parser.enclosingNode,
							parsedUnit,
							e.qualifiedBinding,
							e.scope,
							e.insideTypeAnnotation);
				}
			}
		}
		return contextAccepted;
	}

	private boolean checkForCNF(TypeReference ref, CompilationUnitDeclaration parsedUnit, boolean showAll) {
		this.lookupEnvironment.buildTypeBindings(parsedUnit, null);
		this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
		parsedUnit.resolve();
		this.startPosition = ref.sourceStart;
		this.endPosition = ref.sourceEnd > ref.sourceStart ? ref.sourceEnd : ref.sourceStart;
		if ((this.unitScope = parsedUnit.scope) != null) {
			if (showAll) {
				char[][] tokens = ref.getTypeName();
				char[] typeName = CharOperation.concatWithAll(tokens, '.');
				if (typeName.length == 0) {
					buildContext(ref, null, parsedUnit, null, null);
					this.completionToken = new char[] {'*'};
					findTypesAndPackages(this.completionToken, this.unitScope, true, true, new ObjectVector());
					return true;
				}
			}
			parsedUnit.scope.faultInTypes();
		}
		return false; // should not come here - will throw exception
	}

	private boolean completeOnPackageVisibilityStatements(boolean contextAccepted,
			CompilationUnitDeclaration parsedUnit, PackageVisibilityStatement[] pvsStmts) {
		try {
			this.skipDefaultPackage = true;
			for (int i = 0, l = pvsStmts.length; i < l; ++i) {
				PackageVisibilityStatement pvs = pvsStmts[i];
				if (pvs instanceof CompletionOnKeywordModuleInfo) { // dummy pvs statement
					contextAccepted = true;
					processModuleKeywordCompletion(parsedUnit, pvs, (CompletionOnKeyword) pvs);
					return contextAccepted;
				}
				if (pvs.pkgRef instanceof CompletionOnPackageVisibilityReference) {
					contextAccepted = true;
					buildContext(pvs, null, parsedUnit, null, null);
					if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
						findPackages((CompletionOnPackageVisibilityReference) pvs.pkgRef);
					}
					debugPrintf();
					return contextAccepted;
				}
				ModuleReference[] targets = pvs.targets;
				if (targets == null) continue;
				HashSet<String> skipSet = new HashSet<>();
				for (int j = 0, lj = targets.length; j < lj; j++) {
					ModuleReference target = targets[j];
					if (target == null) break;
					if (target instanceof CompletionOnModuleReference) {
						buildContext(target, null, parsedUnit, null, null);
						contextAccepted = true;
						if(!this.requestor.isIgnored(CompletionProposal.MODULE_REF)) {
							findTargettedModules((CompletionOnModuleReference) target, skipSet);
						}
						debugPrintf();
						return contextAccepted;
					} else if (target instanceof CompletionOnKeyword) {
						contextAccepted = true;
						processModuleKeywordCompletion(parsedUnit, target, (CompletionOnKeyword) target);
					} else {
					if (target.moduleName != null || target.moduleName == CharOperation.NO_CHAR)
							skipSet.add(new String(target.moduleName));
					}
				}
			}
		} finally {
			this.skipDefaultPackage = false;
		}
		return contextAccepted;
	}

	private void debugPrintf() {
		if(this.noProposal && this.problem != null) {
			this.requestor.completionFailure(this.problem);
			if(DEBUG) {
				this.printDebug(this.problem);
			}
		}
	}

	private void processModuleKeywordCompletion(CompilationUnitDeclaration parsedUnit, ASTNode node, CompletionOnKeyword keyword) {
		buildContext(node, null, parsedUnit, null, null);
		if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
			setSourceAndTokenRange(node.sourceStart, node.sourceEnd);
			findKeywords(keyword.getToken(), keyword.getPossibleKeywords(), false, parsedUnit.currentPackage != null);
		}
		debugPrintf();
	}

	public void complete(IType type, char[] snippet, int position, char[][] localVariableTypeNames, char[][] localVariableNames, int[] localVariableModifiers, boolean isStatic){
		if(this.requestor != null){
			this.requestor.beginReporting();
		}
		boolean contextAccepted = false;
		IType topLevelType = type;
		while(topLevelType.getDeclaringType() != null) {
			topLevelType = topLevelType.getDeclaringType();
		}

		this.fileName = topLevelType.getParent().getElementName().toCharArray();
		CompilationResult compilationResult = new CompilationResult(this.fileName, 1, 1, this.compilerOptions.maxProblemsPerUnit);

		CompilationUnitDeclaration compilationUnit = null;

		try {
			// TypeConverter is used instead of SourceTypeConverter because the type
			// to convert can be a binary type or a source type
			TypeDeclaration typeDeclaration = null;
			if (type instanceof SourceType) {
				SourceType sourceType = (SourceType) type;
				ISourceType info = (ISourceType) sourceType.getElementInfo();
				compilationUnit = SourceTypeConverter.buildCompilationUnit(
					new ISourceType[] {info},//sourceTypes[0] is always toplevel here
					SourceTypeConverter.FIELD_AND_METHOD // need field and methods
					| SourceTypeConverter.MEMBER_TYPE, // need member types
					// no need for field initialization
					this.problemReporter,
					compilationResult);
				if (compilationUnit != null && compilationUnit.types != null)
					typeDeclaration = compilationUnit.types[0];
			} else {
				compilationUnit = new CompilationUnitDeclaration(this.problemReporter, compilationResult, 0);
				typeDeclaration = new BinaryTypeConverter(this.parser.problemReporter(), compilationResult, null/*no need to remember type names*/).buildTypeDeclaration(type, compilationUnit);
			}

			if(typeDeclaration != null) {
				// build AST from snippet
				Initializer fakeInitializer = parseSnippeInitializer(snippet, position, localVariableTypeNames, localVariableNames, localVariableModifiers, isStatic);

				// merge AST
				FieldDeclaration[] oldFields = typeDeclaration.fields;
				FieldDeclaration[] newFields = null;
				if (oldFields != null) {
					newFields = new FieldDeclaration[oldFields.length + 1];
					System.arraycopy(oldFields, 0, newFields, 0, oldFields.length);
					newFields[oldFields.length] = fakeInitializer;
				} else {
					newFields = new FieldDeclaration[] {fakeInitializer};
				}
				typeDeclaration.fields = newFields;

				if(DEBUG) {
					trace("SNIPPET COMPLETION AST :"); //$NON-NLS-1$
					trace(compilationUnit.toString());
				}

				if (compilationUnit.types != null) {
					try {
						this.lookupEnvironment.buildTypeBindings(compilationUnit, null /*no access restriction*/);

						if ((this.unitScope = compilationUnit.scope) != null) {
							this.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
							contextAccepted =
								complete(
									e.astNode,
									this.parser.assistNodeParent,
									this.parser.enclosingNode,
									compilationUnit,
									e.qualifiedBinding,
									e.scope,
									e.insideTypeAnnotation);
						}
					}
				}
				if(this.noProposal && this.problem != null) {
					if(!contextAccepted) {
						contextAccepted = true;
						InternalCompletionContext context = new InternalCompletionContext();
						if (this.requestor.isExtendedContextRequired()) context.setExtended();
						this.requestor.acceptContext(context);
					}
					this.requestor.completionFailure(this.problem);
					if(DEBUG) {
						this.printDebug(this.problem);
					}
				}
			}
		}  catch (IndexOutOfBoundsException | InvalidCursorLocation | AbortCompilation | CompletionNodeFound e){ // internal failure - bugs 5618 (added with fix of 99629)
			if(DEBUG) {
				trace("Exception caught by CompletionEngine:", e); //$NON-NLS-1$
			}
		} catch(JavaModelException e) {
			// Do nothing
		}
		if(!contextAccepted) {
			contextAccepted = true;
			InternalCompletionContext context = new InternalCompletionContext();
			if (this.requestor.isExtendedContextRequired()) context.setExtended();
			this.requestor.acceptContext(context);
		}
		if(this.requestor != null){
			this.requestor.endReporting();
		}
	}

	private void completionOnBreakStatement(CompletionOnBreakStatement astNode) {
		if (!this.requestor.isIgnored(CompletionProposal.LABEL_REF)) {
			this.completionToken = astNode.label;
			findLabels(this.completionToken, astNode.possibleLabels);
		}
	}

	private void completionOnContinueStatement(CompletionOnContinueStatement astNode) {
		if (!this.requestor.isIgnored(CompletionProposal.LABEL_REF)) {
			this.completionToken = astNode.label;
			findLabels(this.completionToken, astNode.possibleLabels);
		}
	}

	private void completionOnClassLiteralAccess(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
			CompletionOnClassLiteralAccess access = (CompletionOnClassLiteralAccess) astNode;
			setSourceAndTokenRange(access.classStart, access.sourceEnd);
			this.completionToken = access.completionIdentifier;
			findClassField(
					this.completionToken,
					(TypeBinding) qualifiedBinding,
					scope,
					null,
					null,
					null,
					false);
		}
	}

	private void completionOnExplicitConstructorCall(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
			setSourceAndTokenRange(astNode.sourceStart, astNode.sourceEnd, false);
			CompletionOnExplicitConstructorCall constructorCall = (CompletionOnExplicitConstructorCall) astNode;
			TypeBinding[] argTypes = computeTypes(constructorCall.arguments);
			findConstructors(
				(ReferenceBinding) qualifiedBinding,
				argTypes,
				scope,
				constructorCall,
				false,
				null,
				null,
				null,
				false);
		}
	}

	private void completionOnFieldName(ASTNode astNode, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
			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;
			}

			this.completionToken = field.realName;


			int kind =
				 (field.modifiers & ClassFileConstants.AccStatic) == 0 ?
						InternalNamingConventions.VK_INSTANCE_FIELD :
							(field.modifiers & ClassFileConstants.AccFinal) == 0 ?
									InternalNamingConventions.VK_STATIC_FIELD :
										InternalNamingConventions.VK_STATIC_FINAL_FIELD;

			findVariableNames(field.realName, field.type, excludeNames, null, kind);
		}
	}

	private void completionOnRecordComponentName(ASTNode astNode, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
			CompletionOnRecordComponentName component = (CompletionOnRecordComponentName) 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;
			}
			this.completionToken = component.realName;
			findVariableNames(component.realName, component.type, excludeNames, null,
					InternalNamingConventions.VK_INSTANCE_FIELD);
		}
	}

	private void completionOnFieldType(ASTNode astNode, Scope scope) {
		CompletionOnFieldType field = (CompletionOnFieldType) astNode;
		CompletionOnSingleTypeReference type = (CompletionOnSingleTypeReference) field.type;
		this.completionToken = type.token;
		setSourceAndTokenRange(type.sourceStart, type.sourceEnd);

		findTypesAndPackages(this.completionToken, scope, true, true, new ObjectVector());
		if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
			findKeywordsForMember(this.completionToken, field.modifiers, astNode);
		}

		if (!field.isLocalVariable && field.modifiers == ClassFileConstants.AccDefault) {
			SourceTypeBinding enclosingType = scope.enclosingSourceType();
			if (!enclosingType.isAnnotationType()) {
				if (!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
					findMethodDeclarations(
							this.completionToken,
							enclosingType,
							scope,
							new ObjectVector(),
							null,
							null,
							null,
							false);
				}
				if (!this.requestor.isIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION)) {
					proposeNewMethod(this.completionToken, enclosingType);
				}
			}
		}
	}
	//TODO
	private void completionOnJavadocAllocationExpression(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		// setSourceRange(astNode.sourceStart, astNode.sourceEnd, false);

		CompletionOnJavadocAllocationExpression allocExpression = (CompletionOnJavadocAllocationExpression) astNode;
		this.javadocTagPosition = allocExpression.tagSourceStart;
		int rangeStart = astNode.sourceStart;
		if (allocExpression.type.isThis()) {
			if (allocExpression.completeInText()) {
				rangeStart = allocExpression.separatorPosition;
			}
		} else if (allocExpression.completeInText()) {
			rangeStart = allocExpression.type.sourceStart;
		}
		setSourceAndTokenRange(rangeStart, astNode.sourceEnd, false);
		TypeBinding[] argTypes = computeTypes(allocExpression.arguments);

		ReferenceBinding ref = (ReferenceBinding) qualifiedBinding;
		if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF) && ref.isClass()) {
			findConstructors(ref, argTypes, scope, allocExpression, false, null, null, null, false);
		}
	}
	//TODO
	private void completionOnJavadocFieldReference(ASTNode astNode, Scope scope) {
		this.insideQualifiedReference = true;
		CompletionOnJavadocFieldReference fieldRef = (CompletionOnJavadocFieldReference) astNode;
		this.completionToken = fieldRef.token;
		long completionPosition = fieldRef.nameSourcePosition;
		this.javadocTagPosition = fieldRef.tagSourceStart;

		if (fieldRef.actualReceiverType != null && fieldRef.actualReceiverType.isValidBinding()) {
				ReferenceBinding receiverType = (ReferenceBinding) fieldRef.actualReceiverType;
			int rangeStart = (int) (completionPosition >>> 32);
			if (fieldRef.receiver.isThis()) {
				if (fieldRef.completeInText()) {
					rangeStart = fieldRef.separatorPosition;
				}
			} else if (fieldRef.completeInText()) {
				rangeStart = fieldRef.receiver.sourceStart;
			}
			setSourceAndTokenRange(rangeStart, (int) completionPosition);

			if (!this.requestor.isIgnored(CompletionProposal.FIELD_REF)
					|| !this.requestor.isIgnored(CompletionProposal.JAVADOC_FIELD_REF)) {
				findFields(this.completionToken,
					receiverType,
					scope,
					new ObjectVector(),
					new ObjectVector(),
					false, /*not only static */
					fieldRef,
					scope,
					false,
					true,
					null,
					null,
					null,
					false,
					null,
					-1,
					-1);
			}

			if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)
					|| !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) {
				findMethods(
					this.completionToken,
					null,
					null,
					receiverType,
					scope,
					new ObjectVector(),
					false, /*not only static */
					false,
					fieldRef,
					scope,
					false,
					false,
					true,
					null,
					null,
					null,
					false,
					null,
					-1,
					-1);
				if (fieldRef.actualReceiverType instanceof ReferenceBinding) {
					ReferenceBinding refBinding = (ReferenceBinding)fieldRef.actualReceiverType;
					if (this.completionToken == null
							|| CharOperation.prefixEquals(this.completionToken, refBinding.sourceName)
							|| (this.options.camelCaseMatch && CharOperation.camelCaseMatch(this.completionToken, refBinding.sourceName))) {
						findConstructors(refBinding, null, scope, fieldRef, false, null, null, null, false);
					}
				}
			}
		}
	}
	//TODO
	private void completionOnJavadocMessageSend(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		CompletionOnJavadocMessageSend messageSend = (CompletionOnJavadocMessageSend) astNode;
		TypeBinding[] argTypes = null; //computeTypes(messageSend.arguments);
		this.completionToken = messageSend.selector;
		this.javadocTagPosition = messageSend.tagSourceStart;

		// Set source range
		int rangeStart = astNode.sourceStart;
		if (messageSend.receiver.isThis()) {
			if (messageSend.completeInText()) {
				rangeStart = messageSend.separatorPosition;
			}
		} else if (messageSend.completeInText()) {
			rangeStart = messageSend.receiver.sourceStart;
		}
		setSourceAndTokenRange(rangeStart, astNode.sourceEnd, false);

		if (qualifiedBinding == null) {
			if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
				findImplicitMessageSends(this.completionToken, argTypes, scope, messageSend, scope, new ObjectVector());
			}
		} else if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
			findMethods(
				this.completionToken,
				null,
				argTypes,
				(ReferenceBinding) ((ReferenceBinding) qualifiedBinding).capture(scope, messageSend.receiver.sourceStart, messageSend.receiver.sourceEnd),
				scope,
				new ObjectVector(),
				false,
				false/* prefix match */,
				messageSend,
				scope,
				false,
				messageSend.receiver instanceof SuperReference,
				true,
				null,
				null,
				null,
				false,
				null,
				-1,
				-1);
		}
	}
	//TODO
	private void completionOnJavadocParamNameReference(ASTNode astNode) {
		if (!this.requestor.isIgnored(CompletionProposal.JAVADOC_PARAM_REF)) {
			CompletionOnJavadocParamNameReference paramRef = (CompletionOnJavadocParamNameReference) astNode;
			setSourceAndTokenRange(paramRef.tagSourceStart, paramRef.tagSourceEnd);
			findJavadocParamNames(paramRef.token, paramRef.missingParams, false);
			findJavadocParamNames(paramRef.token, paramRef.missingTypeParams, true);
		}
	}
	private void completionOnJavadocQualifiedTypeReference(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		completionOnJavadocQualifiedTypeReference(astNode, qualifiedBinding, scope, true);
	}
	//TODO
	private void completionOnJavadocQualifiedTypeReference(ASTNode astNode, Binding qualifiedBinding, Scope scope, boolean findModules) {
		this.insideQualifiedReference = true;

		CompletionOnJavadocQualifiedTypeReference typeRef = (CompletionOnJavadocQualifiedTypeReference) astNode;
		this.completionToken = typeRef.completionIdentifier;
		long completionPosition = typeRef.sourcePositions[typeRef.tokens.length];
		this.javadocTagPosition = typeRef.tagSourceStart;

		// get the source positions of the completion identifier
		if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) {
			if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF) ||
					((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_TYPE_REF))) {
				int rangeStart = typeRef.completeInText() ? typeRef.sourceStart : (int) (completionPosition >>> 32);
				setSourceAndTokenRange(rangeStart, (int) completionPosition);
				findMemberTypes(
					this.completionToken,
					(ReferenceBinding) qualifiedBinding,
					scope,
					scope.enclosingSourceType(),
					false,
					false,
					new ObjectVector(),
					null,
					null,
					null,
					false);
			}
		} else if (qualifiedBinding instanceof PackageBinding) {

			setSourceRange(astNode.sourceStart, (int) completionPosition);
			int rangeStart = typeRef.completeInText() ? typeRef.sourceStart : (int) (completionPosition >>> 32);
			setTokenRange(rangeStart, (int) completionPosition);
			// replace to the end of the completion identifier
			findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding, scope);
		}
		if (findModules) {
			findModules(typeRef, false);
		}
	}
	//TODO
	private void completionOnJavadocSingleTypeReference(ASTNode astNode, Scope scope) {
		CompletionOnJavadocSingleTypeReference typeRef = (CompletionOnJavadocSingleTypeReference) astNode;
		this.completionToken = typeRef.token;
		this.javadocTagPosition = typeRef.tagSourceStart;
		setSourceAndTokenRange(typeRef.sourceStart, typeRef.sourceEnd);
		findTypesAndPackages(
				this.completionToken,
				scope,
				(this.assistNodeInJavadoc & CompletionOnJavadoc.BASE_TYPES) != 0,
				false,
				new ObjectVector());
		findModules(typeRef, false);
	}
	//TODO
	private void completionOnJavadocModuleReference(ASTNode astNode,  Binding qualifiedBinding, Scope scope) {
		CompletionOnJavadocModuleReference modRef = (CompletionOnJavadocModuleReference) astNode;
		this.completionToken = modRef.completionIdentifier;
		this.javadocTagPosition = modRef.tagSourceStart;
		setSourceAndTokenRange(modRef.sourceStart, modRef.sourceEnd);
		char[] completionName=  CharOperation.NO_CHAR;
		TypeReference tRef= modRef.getTypeReference();
		if (tRef instanceof CompletionOnJavadocSingleTypeReference) {
			CompletionOnJavadocSingleTypeReference typeRef = (CompletionOnJavadocSingleTypeReference) tRef;
			this.completionToken = typeRef.token;
			this.javadocTagPosition = typeRef.tagSourceStart;
			setSourceAndTokenRange(typeRef.sourceStart, typeRef.sourceEnd);
			completionName= this.completionToken;
			if (completionName.length == 1 && completionName[0] == '/') {
				completionName=  CharOperation.NO_CHAR;
			}
		} else if (tRef instanceof CompletionOnJavadocQualifiedTypeReference){
			completionOnJavadocQualifiedTypeReference(tRef, qualifiedBinding, scope, false);
			return;
		}
		boolean setIgnorePackageRef= true;
		try {
			if (this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
				setIgnorePackageRef = false;
				this.requestor.setIgnored(CompletionProposal.PACKAGE_REF, setIgnorePackageRef);
			}
			setSourceAndTokenRange(modRef.moduleReference.sourceStart, modRef.moduleReference.sourceEnd);
			findPackagesInModule(CharOperation.toLowerCase(completionName), modRef.moduleReference.binding, scope);
		} finally {
			if (!setIgnorePackageRef) {
				this.requestor.setIgnored(CompletionProposal.PACKAGE_REF, !setIgnorePackageRef);
			}
		}
	}
	//TODO
	private void completionOnJavadocTag(ASTNode astNode) {
		CompletionOnJavadocTag javadocTag = (CompletionOnJavadocTag) astNode;
		setSourceAndTokenRange(javadocTag.tagSourceStart, javadocTag.sourceEnd);
		findJavadocBlockTags(javadocTag);
		findJavadocInlineTags(javadocTag);
		findJavadocInSnippetTags(javadocTag);

	}
	//TODO
	private void completionOnJavadocTypeParamReference(ASTNode astNode) {
		if (!this.requestor.isIgnored(CompletionProposal.JAVADOC_PARAM_REF)) {
			CompletionOnJavadocTypeParamReference paramRef = (CompletionOnJavadocTypeParamReference) astNode;
			setSourceAndTokenRange(paramRef.tagSourceStart, paramRef.tagSourceEnd);
			findJavadocParamNames(paramRef.token, paramRef.missingParams, true);
		}
	}

	private void completionOnKeyword(ASTNode astNode) {
		if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
			CompletionOnKeyword keyword = (CompletionOnKeyword)astNode;
			findKeywords(keyword.getToken(), keyword.getPossibleKeywords(), false, false);
		}
	}

	private void completionOnLocalOrArgumentName(ASTNode astNode, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
			LocalDeclaration variable = (LocalDeclaration) astNode;

			int kind;
			if (variable instanceof CompletionOnLocalName){
				this.completionToken = ((CompletionOnLocalName) variable).realName;
				kind = InternalNamingConventions.VK_LOCAL;
			} else {
				CompletionOnArgumentName arg = (CompletionOnArgumentName) variable;
				this.completionToken = arg.realName;
				if ((variable.bits & ASTNode.IsTypeElided) != 0) {
					findKeywords(variable.name, new char[][] { Keywords.VAR }, false, false);
					return; // heuristics below need a type, which we don't have here
				}
				kind = arg.isCatchArgument ? InternalNamingConventions.VK_LOCAL : InternalNamingConventions.VK_PARAMETER;
			}

			char[][] alreadyDefinedName = computeAlreadyDefinedName((BlockScope)scope, variable);

			char[][] forbiddenNames = findVariableFromUnresolvedReference(variable, (BlockScope)scope, alreadyDefinedName);

			LocalVariableBinding[] locals = ((BlockScope)scope).locals;
			char[][] discouragedNames = new char[locals.length][];
			int localCount = 0;
			for(int i = 0 ; i < locals.length ; i++){
				if (locals[i] != null) {
					discouragedNames[localCount++] = locals[i].name;
				}
			}

			System.arraycopy(discouragedNames, 0, discouragedNames = new char[localCount][], 0, localCount);

			findVariableNames(this.completionToken, variable.type, discouragedNames, forbiddenNames, kind);
		}
	}

	private void completionOnMarkerAnnotationName(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		CompletionOnMarkerAnnotationName annot = (CompletionOnMarkerAnnotationName) astNode;

		// When completion is inside lambda body, the fake type cannot be attached to the lambda.
		ReferenceContext referenceContext = scope.parent.referenceContext();
		CompletionOnAnnotationOfType fakeType;
		if (referenceContext instanceof CompletionOnAnnotationOfType) {
			fakeType = (CompletionOnAnnotationOfType) referenceContext;
		} else {
			fakeType = new CompletionOnAnnotationOfType(CompletionParser.FAKE_TYPE_NAME, scope.referenceCompilationUnit().compilationResult, annot);
		}
		if (fakeType.annotations[0] == annot) {
			// When the completion is inside a method body the annotation cannot be accuratly attached to the correct node by completion recovery.
			// So 'targetedElement' is not computed in this case.
			if (scope.parent.parent == null || !(scope.parent.parent instanceof MethodScope)) {
				this.targetedElement = computeTargetedElement(fakeType);
			}

		}

		this.assistNodeIsAnnotation = true;
		if (annot.type instanceof CompletionOnSingleTypeReference) {
			CompletionOnSingleTypeReference type = (CompletionOnSingleTypeReference) annot.type;
			this.completionToken = type.token;
			setSourceAndTokenRange(type.sourceStart, type.sourceEnd);

			if (scope.parent.parent != null &&
					!(scope.parent.parent instanceof MethodScope) && !fakeType.isParameter) {

				if (this.completionToken.length <= Keywords.INTERFACE.length
					&& CharOperation.prefixEquals(this.completionToken, Keywords.INTERFACE, false /* ignore case */
				)){
					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForResolution();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(this.completionToken, Keywords.INTERFACE);
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywords
					relevance += R_ANNOTATION; // this proposal is most relevant than annotation proposals

					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
						CompletionProposal proposal = createProposal(CompletionProposal.KEYWORD, this.actualCompletionPosition);
						proposal.setName(Keywords.INTERFACE);
						proposal.setCompletion(Keywords.INTERFACE);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}

			findTypesAndPackages(this.completionToken, scope, false, false, new ObjectVector());
		} else if (annot.type instanceof CompletionOnQualifiedTypeReference) {
			this.insideQualifiedReference = true;

			CompletionOnQualifiedTypeReference type = (CompletionOnQualifiedTypeReference) annot.type;
			this.completionToken = type.completionIdentifier;
			long completionPosition = type.sourcePositions[type.tokens.length];
			if (qualifiedBinding instanceof PackageBinding) {

				setSourceRange(astNode.sourceStart, (int) completionPosition);
				setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
				// replace to the end of the completion identifier
				findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding, scope);
			} else {
				setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);

				findMemberTypes(
					this.completionToken,
					(ReferenceBinding) qualifiedBinding,
					scope,
					scope.enclosingSourceType(),
					false,
					false,
					new ObjectVector(),
					null,
					null,
					null,
					false);
			}
		}
	}

	private void completionOnMemberAccess(ASTNode astNode, ASTNode enclosingNode, Binding qualifiedBinding,
			Scope scope, boolean insideTypeAnnotation) {
		this.insideQualifiedReference = true;
		CompletionOnMemberAccess access = (CompletionOnMemberAccess) astNode;
		long completionPosition = access.nameSourcePosition;
		setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);

		this.completionToken = access.token;

		if (qualifiedBinding.problemId() == ProblemReasons.NotFound) {
			// complete method members with missing return type
			// class X {
			//   Missing f() {return null;}
			//   void foo() {
			//     f().|
			//   }
			// }
			if (this.assistNodeInJavadoc == 0 &&
					(this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) ||
							this.requestor.isAllowingRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF))) {
				ProblemMethodBinding problemMethodBinding = (ProblemMethodBinding) qualifiedBinding;
				findFieldsAndMethodsFromMissingReturnType(
						problemMethodBinding.selector,
						problemMethodBinding.parameters,
						scope,
						access,
						insideTypeAnnotation);
			}
		} else {
			if (!access.isInsideAnnotation) {
				if (!this.requestor.isIgnored(CompletionProposal.KEYWORD) && !access.isSuperAccess()) {
					findKeywords(this.completionToken, new char[][]{Keywords.NEW}, false, false);
				}

				ObjectVector fieldsFound = new ObjectVector();
				ObjectVector methodsFound = new ObjectVector();

				boolean superCall = access.receiver instanceof SuperReference;

				findFieldsAndMethods(
					this.completionToken,
					((TypeBinding) qualifiedBinding).capture(scope, access.receiver.sourceStart, access.receiver.sourceEnd),
					scope,
					fieldsFound,
					methodsFound,
					access,
					scope,
					false,
					superCall,
					null,
					null,
					null,
					false,
					null,
					-1,
					-1);

				if (!superCall) {

					checkCancel();

					findFieldsAndMethodsFromCastedReceiver(
							enclosingNode,
							qualifiedBinding,
							scope,
							fieldsFound,
							methodsFound,
							access,
							scope,
							access.receiver);
				}
			}
		}
	}

	private void completionOnMemberValueName(ASTNode astNode, ASTNode astNodeParent, Scope scope,
			boolean insideTypeAnnotation) {
		CompletionOnMemberValueName memberValuePair = (CompletionOnMemberValueName) astNode;
		Annotation annotation = (Annotation) astNodeParent;

		this.completionToken = memberValuePair.name;

		ReferenceBinding annotationType = (ReferenceBinding)annotation.resolvedType;

		if (annotationType != null && annotationType.isAnnotationType()) {
			if (!this.requestor.isIgnored(CompletionProposal.ANNOTATION_ATTRIBUTE_REF)) {
				findAnnotationAttributes(this.completionToken, annotation.memberValuePairs(), annotationType);
			}
			if (this.assistNodeCanBeSingleMemberAnnotation) {
				if (this.expectedTypesPtr > -1 && this.expectedTypes[0].isAnnotationType()) {
					findTypesAndPackages(this.completionToken, scope, false, false, new ObjectVector());
				} else {
					if (this.expectedTypesPtr > -1) {
						this.assistNodeIsEnum = true;
						done : for (int i = 0; i <= this.expectedTypesPtr; i++) {
							if (!this.expectedTypes[i].isEnum()) {
								this.assistNodeIsEnum = false;
								break done;
							}
						}

					}
					if (scope instanceof BlockScope && !this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
						char[][] alreadyDefinedName = computeAlreadyDefinedName((BlockScope)scope, FakeInvocationSite);

						findUnresolvedReference(
								memberValuePair.sourceStart,
								memberValuePair.sourceEnd,
								(BlockScope)scope,
								alreadyDefinedName);
					}
					findVariablesAndMethods(
						this.completionToken,
						scope,
						FakeInvocationSite,
						scope,
						insideTypeAnnotation,
						true);
					// can be the start of a qualified type name
					findTypesAndPackages(this.completionToken, scope, false, false, new ObjectVector());
				}
			}
		}
	}

	private void completionOnMessageSend(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		setSourceAndTokenRange(astNode.sourceStart, astNode.sourceEnd, false);

		CompletionOnMessageSend messageSend = (CompletionOnMessageSend) astNode;
		TypeBinding[] argTypes = computeTypes(messageSend.arguments);
		this.completionToken = messageSend.selector;
		ObjectVector methodsFound = new ObjectVector();
		if (qualifiedBinding == null) {
			if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {

				findImplicitMessageSends(this.completionToken, argTypes, scope, messageSend, scope, methodsFound);

				checkCancel();

				findLocalMethodsFromStaticImports(
						this.completionToken,
						scope,
						messageSend,
						scope,
						true,
						methodsFound,
						true);
			}
		} else  if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
			findMethods(
				this.completionToken,
				null,
				argTypes,
				(ReferenceBinding)((ReferenceBinding) qualifiedBinding).capture(scope, messageSend.receiver.sourceStart, messageSend.receiver.sourceEnd),
				scope,
				methodsFound,
				false,
				true,
				messageSend,
				scope,
				false,
				messageSend.receiver instanceof SuperReference,
				false,
				null,
				null,
				null,
				false,
				null,
				-1,
				-1);
		}

		findCompletionsForArgumentPosition(methodsFound, findCompletionArgIndex(messageSend.arguments), scope);
	}

	private int findCompletionArgIndex(Expression[] arguments) {
		if (arguments == null) {
			return 0;
		}
		for (int index = 0; index < arguments.length; index++) {
			if ((arguments[index] instanceof CompletionNode)
					// the following handles when the cursor is at the receiver in a completed expression like
					// new PersonDetails(GH969.emptyList(), 0) here the cursor is after new PersonDetails(
					|| (arguments[index] instanceof MessageSend ms && ms.receiver instanceof CompletionNode)
					|| (arguments[index] instanceof FieldReference fr && fr.receiver instanceof CompletionNode)) {
				return index;
			}
		}
		return arguments.length;
	}

	private void findCompletionsForArgumentPosition(ObjectVector methodsFound, int completedArgumentLength, Scope scope) {
		if (methodsFound.size() == 0) {
			return;
		}
		pushExpectedTypesForArgumentPosition(methodsFound, completedArgumentLength, scope);
		this.strictMatchForExtepectedType = true;
		int filter = this.expectedTypesFilter;
		this.expectedTypesFilter = SUBTYPE;
		int start = this.startPosition, end = this.endPosition;
		int tStart = this.tokenStart, tEnd = this.tokenEnd;
		try {
			this.startPosition = this.endPosition = this.tokenStart = this.tokenEnd = this.actualCompletionPosition + 1;
			findVariablesAndMethods(CharOperation.NO_CHAR, scope, FakeInvocationSite, scope, false, false, false, methodsFound);
		} finally {
			this.startPosition = start;
			this.endPosition = end;
			this.tokenStart = tStart;
			this.tokenEnd = tEnd;
			this.strictMatchForExtepectedType = false;
			this.expectedTypesFilter = filter;
		}
	}

	private void pushExpectedTypesForArgumentPosition(ObjectVector methodsToSearchOn, int completedArgumentLength,
			Scope scope) {
		if (methodsToSearchOn.size == 0) {
			return;
		}
		for (int i = 0; i < methodsToSearchOn.size; i++) {
			MethodBinding method = (MethodBinding) ((Object[]) methodsToSearchOn.elementAt(i))[0];
			boolean onOrAfterLastParam = method.parameters.length <= completedArgumentLength;
			if (onOrAfterLastParam && !method.isVarargs()) {
				continue;
			}

			int index = onOrAfterLastParam
					? method.parameters.length - 1
					: completedArgumentLength;

			TypeBinding paramType = method.parameters[index];
			addExpectedType(paramType, scope);
			if (method.isVarargs() && paramType.isArrayType() && index == (method.parameters.length - 1)) {
				addExpectedType(((ArrayBinding) paramType).elementsType(), scope);
			}
		}
	}

	private void completionOnMessageSendName(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
			CompletionOnMessageSendName messageSend = (CompletionOnMessageSendName) astNode;

			setTokenRange(messageSend.sourceStart, messageSend.sourceEnd);
			if (messageSend.statementEnd > messageSend.sourceStart)
				setSourceRange(messageSend.sourceStart, messageSend.statementEnd);

			this.completionToken = messageSend.selector;

			if (messageSend.nextIsCast) {
				// optionalPrefix|((String) s) was mistaken as a messageSend(?). Treat like beginning of statement.
				findVariablesAndMethods(this.completionToken, scope, messageSend, scope, false, false);
				findTypesAndPackages(this.completionToken, scope, true, false, new ObjectVector());
				return;
			}

			this.insideQualifiedReference = true;

			TypeBinding receiverType = (TypeBinding)qualifiedBinding;

			if(receiverType != null && receiverType instanceof ReferenceBinding) {
				TypeBinding[] typeArgTypes = computeTypesIfCorrect(messageSend.typeArguments);
				if(typeArgTypes != null) {
					findMethods(
							this.completionToken,
							typeArgTypes,
							null,
							(ReferenceBinding)receiverType.capture(scope, messageSend.receiver.sourceStart, messageSend.receiver.sourceEnd),
							scope,
							new ObjectVector(),
							messageSend.receiver.isType(),
							false,
							messageSend,
							scope,
							false,
							false,
							false,
							null,
							null,
							null,
							false,
							null,
							-1,
							-1);
				}
			}
		}
	}
	private void completionOnReferenceExpressionName(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.METHOD_NAME_REFERENCE)) {
			CompletionOnReferenceExpressionName referenceExpression = (CompletionOnReferenceExpressionName) astNode;
			this.insideQualifiedReference = true;
			this.completionToken = referenceExpression.selector;
			boolean onlyStatic = false;

			TypeBinding receiverType = (TypeBinding) qualifiedBinding;
			if (receiverType != null && receiverType instanceof ReferenceBinding) {
				setSourceAndTokenRange(referenceExpression.nameSourceStart, referenceExpression.sourceEnd);
				if (!(receiverType.isInterface() || this.requestor.isIgnored(CompletionProposal.KEYWORD))) {
					this.assistNodeIsConstructor = true;
					findKeywords(this.completionToken, new char[][] { Keywords.NEW }, false, false);
				}
				findMethods(
						this.completionToken,
						referenceExpression.resolvedTypeArguments,
						null,
						(ReferenceBinding)receiverType.capture(scope, referenceExpression.sourceStart, referenceExpression.sourceEnd),
						scope,
						new ObjectVector(),
						onlyStatic,
						false,
						referenceExpression,
						scope,
						false,
						false,
						false,
						null,
						null,
						null,
						false,
						null,
						-1,
						-1);
			} else if (!this.requestor.isIgnored(CompletionProposal.KEYWORD) && receiverType != null
					&& receiverType instanceof ArrayBinding) {
				setSourceAndTokenRange(referenceExpression.nameSourceStart, referenceExpression.sourceEnd);
				findKeywords(this.completionToken, new char[][] { Keywords.NEW }, false, false);
			}
		}
	}


	private void completionOnMethodName(ASTNode astNode, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
			CompletionOnMethodName method = (CompletionOnMethodName) astNode;

			setSourceAndTokenRange(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;
			}

			this.completionToken = method.selector;


			int kind =
				 (method.modifiers & ClassFileConstants.AccStatic) == 0 ?
						InternalNamingConventions.VK_INSTANCE_FIELD :
							(method.modifiers & ClassFileConstants.AccFinal) == 0 ?
									InternalNamingConventions.VK_STATIC_FIELD :
										InternalNamingConventions.VK_STATIC_FINAL_FIELD;

			findVariableNames(this.completionToken, method.returnType, excludeNames, null, kind);
		}
	}

	private void completionOnMethodReturnType(ASTNode astNode, Scope scope) {
		CompletionOnMethodReturnType method = (CompletionOnMethodReturnType) astNode;
		SingleTypeReference type = (CompletionOnSingleTypeReference) method.returnType;
		this.completionToken = type.token;
		setSourceAndTokenRange(type.sourceStart, type.sourceEnd);
		findTypesAndPackages(this.completionToken, scope.parent, true, true, new ObjectVector());
		if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
			findKeywordsForMember(this.completionToken, method.modifiers, null);
		}

		if (method.modifiers == ClassFileConstants.AccDefault) {
			SourceTypeBinding enclosingType = scope.enclosingSourceType();
			if (!enclosingType.isAnnotationType()) {
				if (!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
					findMethodDeclarations(
							this.completionToken,
							scope.enclosingSourceType(),
							scope,
							new ObjectVector(),
							null,
							null,
							null,
							false);
				}
				if (!this.requestor.isIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION)) {
					proposeNewMethod(this.completionToken, scope.enclosingSourceType());
				}
			}
		}
	}

	private void completionOnParameterizedQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
		if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			CompletionOnParameterizedQualifiedTypeReference ref = (CompletionOnParameterizedQualifiedTypeReference) astNode;

			this.insideQualifiedReference = true;

			this.assistNodeIsClass = ref.isClass();
			this.assistNodeIsException = ref.isException();
			this.assistNodeIsInterface = ref.isInterface();
			this.assistNodeIsSuperType = ref.isSuperType();
			this.assistNodeIsExtendedType = assistNodeIsExtendedType(astNode, astNodeParent);
			this.assistNodeIsInterfaceExcludingAnnotation = assistNodeIsInterfaceExcludingAnnotation(astNode, astNodeParent);

			this.completionToken = ref.completionIdentifier;
			long completionPosition = ref.sourcePositions[ref.tokens.length];
			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);

			if (qualifiedBinding.problemId() == ProblemReasons.NotFound ||
					(((ReferenceBinding)qualifiedBinding).tagBits & TagBits.HasMissingType) != 0) {
				if (this.assistNodeInJavadoc == 0 &&
						(this.requestor.isAllowingRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF))) {
					if(ref.tokens.length == 1) {
						findMemberTypesFromMissingType(
								ref,
								ref.sourcePositions[0],
								scope);
					}
				}
			} else {
				ObjectVector typesFound = new ObjectVector();
				if (this.assistNodeIsException && astNodeParent instanceof TryStatement) {
					findExceptionFromTryStatement(
							this.completionToken,
							(ReferenceBinding)qualifiedBinding,
							scope.enclosingSourceType(),
							(BlockScope)scope,
							typesFound);
				}

				checkCancel();

				findMemberTypes(
					this.completionToken,
					(ReferenceBinding) qualifiedBinding,
					scope,
					scope.enclosingSourceType(),
					false,
					false,
					typesFound,
					null,
					null,
					null,
					false);
			}
		}
	}

	private boolean assistNodeIsExtendedType(ASTNode astNode, ASTNode astNodeParent) {
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=99399, don't propose final types for extension.
		if (astNodeParent == null)
			return false;
		if (astNodeParent instanceof TypeDeclaration) {
			TypeDeclaration typeDeclaration = (TypeDeclaration) astNodeParent;
			return (typeDeclaration.superclass == astNode);
		} else if (astNodeParent instanceof TypeParameter) {
			TypeParameter typeParameter = (TypeParameter) astNodeParent;
			return (typeParameter.type == astNode);
		} else if (astNodeParent instanceof Wildcard) {
			Wildcard wildcard = (Wildcard) astNodeParent;
			return (wildcard.bound == astNode && wildcard.kind == Wildcard.EXTENDS);
		}
		return false;
	}

	private boolean assistNodeIsInterfaceExcludingAnnotation(ASTNode astNode, ASTNode astNodeParent) {
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=310423, don't propose annotations for implements.
		if (astNodeParent == null)
			return false;
		if (astNodeParent instanceof TypeDeclaration) {
			TypeDeclaration typeDeclaration = (TypeDeclaration) astNodeParent;
			TypeReference [] superInterfaces = typeDeclaration.superInterfaces;
			int length = superInterfaces == null ? 0 : superInterfaces.length;
			for (int i = 0; i < length; i++) {
				if (superInterfaces[i] == astNode)
					return true;
			}
		}
		return false;
	}

	private boolean assistNodeIsInsideCase(ASTNode astNode, ASTNode astNodeParent) {
		// To find whether we're completing inside the case expression in a
		// switch case construct (https://bugs.eclipse.org/bugs/show_bug.cgi?id=195346)
		if (astNodeParent instanceof SwitchStatement) {
			CaseStatement[] cases = ((SwitchStatement) astNodeParent).cases;
			for (int i = 0, caseCount = ((SwitchStatement) astNodeParent).caseCount; i < caseCount; i++) {
				CompletionNodeDetector detector = new CompletionNodeDetector(astNode, cases[i]);
				if (detector.containsCompletionNode()) {
					return true;
				}
			}
		}
		return false;
	}

	private void completionOnQualifiedAllocationExpression(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
		setSourceAndTokenRange(astNode.sourceStart, astNode.sourceEnd, false);

		CompletionOnQualifiedAllocationExpression allocExpression =
			(CompletionOnQualifiedAllocationExpression) astNode;
		TypeBinding[] argTypes = computeTypes(allocExpression.arguments);

		ReferenceBinding ref = (ReferenceBinding) qualifiedBinding;
		ObjectVector constructorsFound = new ObjectVector();

		if (ref.problemId() == ProblemReasons.NotFound) {
			findConstructorsFromMissingType(
					allocExpression.type,
					argTypes,
					scope,
					allocExpression);
		} else {
			if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)
					&& ref.isClass()
					&& !ref.isAbstract()) {
					findConstructors(
						ref,
						argTypes,
						scope,
						allocExpression,
						false,
						null,
						null,
						null,
						false,
						constructorsFound,
						false);
			}

			checkCancel();

			if (!this.requestor.isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION)
					&& !ref.isFinal()
					&& !ref.isEnum()){
				findAnonymousType(
					ref,
					argTypes,
					scope,
					allocExpression,
					null,
					null,
					null,
					false);
			}
		}
		findCompletionsForArgumentPosition(constructorsFound, findCompletionArgIndex(allocExpression.arguments), scope);
	}

	private void completionOnQualifiedNameReference(ASTNode astNode, ASTNode enclosingNode, Binding qualifiedBinding,
			Scope scope, boolean insideTypeAnnotation) {
		this.insideQualifiedReference = true;
		CompletionOnQualifiedNameReference ref =
			(CompletionOnQualifiedNameReference) astNode;
		this.completionToken = ref.completionIdentifier;
		internalCompletionOnQualifiedReference(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation,
				ref.isInsideAnnotationAttribute, ref, ref.tokens, ref.sourcePositions);
	}

	/** Unified handling for true QualifiedNameReference and misclassified QualifiedTypeReference. */
	private void internalCompletionOnQualifiedReference(ASTNode ref, ASTNode enclosingNode, Binding qualifiedBinding, Scope scope,
			boolean insideTypeAnnotation, boolean isInsideAnnotationAttribute, InvocationSite site, char[][] tokens, long[] sourcePositions)
	{
		long completionPosition = sourcePositions[sourcePositions.length - 1];
		if (qualifiedBinding.problemId() == ProblemReasons.NotFound) {
			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
			// complete field members with missing fields type
			// class X {
			//   Missing f;
			//   void foo() {
			//     f.|
			//   }
			// }
			if (this.assistNodeInJavadoc == 0 &&
					(this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) ||
							this.requestor.isAllowingRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF) ||
							this.requestor.isAllowingRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF))) {
				if(tokens.length == 1) {
					boolean foundSomeFields = findFieldsAndMethodsFromMissingFieldType(tokens[0], scope, site, insideTypeAnnotation);

					if (!foundSomeFields) {

						checkCancel();

						findMembersFromMissingType(
								tokens[0],
								sourcePositions[0],
								null,
								scope,
								site,
								isInsideAnnotationAttribute);
					}
				}
			}
		} else if (qualifiedBinding instanceof VariableBinding) {
			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
			TypeBinding receiverType = ((VariableBinding) qualifiedBinding).type;
			if (receiverType != null && (receiverType.tagBits & TagBits.HasMissingType) == 0) {
				ObjectVector fieldsFound = new ObjectVector();
				ObjectVector methodsFound = new ObjectVector();

				findFieldsAndMethods(
						this.completionToken,
						receiverType.capture(scope, ref.sourceStart, ref.sourceEnd),
						scope,
						fieldsFound,
						methodsFound,
						site,
						scope,
						false,
						false,
						null,
						null,
						null,
						false,
						null,
						-1,
						-1);

				checkCancel();

				if (ref instanceof Expression) {
					findFieldsAndMethodsFromCastedReceiver(
						enclosingNode,
						qualifiedBinding,
						scope,
						fieldsFound,
						methodsFound,
						site,
						scope,
						(Expression) ref);
				}

			} else if (this.assistNodeInJavadoc == 0 &&
					(this.requestor.isAllowingRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF) ||
							this.requestor.isAllowingRequiredProposals(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_REF))) {
				boolean proposeField = !this.requestor.isIgnored(CompletionProposal.FIELD_REF);
				boolean proposeMethod = !this.requestor.isIgnored(CompletionProposal.METHOD_REF);
				if (proposeField || proposeMethod) {
					if(tokens.length == 1) {
						if (qualifiedBinding instanceof LocalVariableBinding) {
							// complete local variable members with missing variables type
							// class X {
							//   void foo() {
							//     Missing f;
							//     f.|
							//   }
							// }
							LocalVariableBinding localVariableBinding = (LocalVariableBinding) qualifiedBinding;
							findFieldsAndMethodsFromMissingType(
									localVariableBinding.declaration.type,
									localVariableBinding.declaringScope,
									site,
									scope);
						} else {
							// complete field members with missing fields type
							// class X {
							//   Missing f;
							//   void foo() {
							//     f.|
							//   }
							// }
							findFieldsAndMethodsFromMissingFieldType(tokens[0], scope, site, insideTypeAnnotation);
						}

					}
				}
			}

		} else if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) {
			ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding;
			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);

			findMembers(
					this.completionToken,
					receiverType,
					scope,
					site,
					isInsideAnnotationAttribute,
					null,
					null,
					null,
					false);

		} else if (qualifiedBinding instanceof PackageBinding) {

			setSourceRange(ref.sourceStart, (int) completionPosition);
			setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);

			// replace to the end of the completion identifier
			findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding, scope);
		}
	}

	private void completionOnQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, ASTNode enclosingNode, Binding qualifiedBinding,
			Scope scope) {
		this.insideQualifiedReference = true;

		CompletionOnQualifiedTypeReference ref =
			(CompletionOnQualifiedTypeReference) astNode;

		this.assistNodeIsClass = ref.isClass();
		this.assistNodeIsException = ref.isException();
		this.assistNodeIsInterface = ref.isInterface();
		this.assistNodeIsConstructor = ref.isConstructorType;
		this.assistNodeIsSuperType = ref.isSuperType();
		this.assistNodeIsExtendedType = assistNodeIsExtendedType(astNode, astNodeParent);
		this.assistNodeIsInterfaceExcludingAnnotation = assistNodeIsInterfaceExcludingAnnotation(astNode, astNodeParent);

		this.completionToken = ref.completionIdentifier;
		long completionPosition = ref.sourcePositions[ref.tokens.length];

		boolean haveTypeProposals = false;

		// get the source positions of the completion identifier
		if (qualifiedBinding.problemId() == ProblemReasons.NotFound) {
			setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
			if (this.assistNodeInJavadoc == 0 &&
					(this.requestor.isAllowingRequiredProposals(CompletionProposal.TYPE_REF, CompletionProposal.TYPE_REF))) {
				if(ref.tokens.length == 1) {
					findMemberTypesFromMissingType(
							ref.tokens[0],
							ref.sourcePositions[0],
							scope);
				}
			}
		} else if (qualifiedBinding instanceof ReferenceBinding && !(qualifiedBinding instanceof TypeVariableBinding)) {
			ReferenceBinding receiverType = (ReferenceBinding) qualifiedBinding;
			if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				setSourceAndTokenRange((int) (completionPosition >>> 32), (int) completionPosition);

				ObjectVector typesFound = new ObjectVector();

				if (this.assistNodeIsException && astNodeParent instanceof TryStatement) {
					findExceptionFromTryStatement(
							this.completionToken,
							receiverType,
							scope.enclosingSourceType(),
							(BlockScope)scope,
							typesFound);
				}

				checkCancel();

				findMemberTypes(
					this.completionToken,
					receiverType,
					scope,
					scope.enclosingSourceType(),
					false,
					false,
					typesFound,
					null,
					null,
					null,
					false);
				haveTypeProposals = typesFound.size() > 0;
			}
		} else if (qualifiedBinding instanceof PackageBinding) {

			setSourceRange(astNode.sourceStart, (int) completionPosition);
			setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
			// replace to the end of the completion identifier
			findTypesAndSubpackages(this.completionToken, (PackageBinding) qualifiedBinding, scope);
		}
		// alternatively interpret tokens in a misclassified LocalDeclaration like a QualifiedNameReference:
		if (!(qualifiedBinding instanceof PackageBinding) && astNodeParent instanceof LocalDeclaration && enclosingNode != null) { // enclosingNode == null when called from completionOnProvidesInterfacesQualifiedTypeReference
			if (scope instanceof BlockScope) {
				// resolve tokens like it's done in CompletionOnQualifiedNameReference:
				qualifiedBinding = ((BlockScope) scope).getBinding(ref.tokens, FakeInvocationSite);
				boolean ignoreType = this.requestor.isIgnored(CompletionProposal.TYPE_REF);
				try {
					this.requestor.setIgnored(CompletionProposal.TYPE_REF, haveTypeProposals); // temp ignore types if already proposed above
					internalCompletionOnQualifiedReference(ref, enclosingNode, qualifiedBinding, scope, false, false, FakeInvocationSite, ref.tokens, ref.sourcePositions);
				} finally {
					this.requestor.setIgnored(CompletionProposal.TYPE_REF, ignoreType);
				}
			}
		}
	}

	private void completionOnProvidesInterfacesQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
		// TODO: Filter the results wrt accessibility and add relevance to the results.
		completionOnQualifiedTypeReference(astNode, astNodeParent, null, qualifiedBinding, scope);
	}

	private void completionOnProvidesImplementationsQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
		findImplementations((ProvidesStatement) this.parser.enclosingNode, (TypeReference) astNode);
	}

	private void completionOnSingleNameReference(ASTNode astNode, ASTNode astNodeParent, Scope scope,
			boolean insideTypeAnnotation) {
		CompletionOnSingleNameReference singleNameReference = (CompletionOnSingleNameReference) astNode;
		this.completionToken = singleNameReference.token;
		SwitchStatement switchStatement = astNodeParent instanceof SwitchStatement ? (SwitchStatement) astNodeParent : null;
		boolean isSwitchEnumOrType = false;
		if((switchStatement != null
				&& switchStatement.expression.resolvedType != null)) {
			TypeBinding resolvedType = switchStatement.expression.resolvedType;
			isSwitchEnumOrType = resolvedType.isEnum();
			if(!isSwitchEnumOrType) {
				if( this.compilerOptions.complianceLevel >= ClassFileConstants.JDK17)
					isSwitchEnumOrType = resolvedType.isClass() || resolvedType.isInterface() || resolvedType.isRecord();
			}

		}
		if (isSwitchEnumOrType) {
			TypeBinding resolvedType = switchStatement.expression.resolvedType;
			if(resolvedType.isEnum()) {
				if (!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
					this.assistNodeIsEnum = true;
					findEnumConstantsFromSwithStatement(this.completionToken, switchStatement);
				}
			}
			else {
					// if switch with class/interface/record - J17 onwards
					char[][] keywords = new char[2][];
					int count = 0;
					if (switchStatement.defaultCase == null) {
						keywords[count++] = Keywords.DEFAULT;
					}
					if (switchStatement.nullCase == null) {
						keywords[count++] = Keywords.NULL;
					}
					System.arraycopy(keywords, 0, keywords = new char[count][], 0, count);
					findKeywords(this.completionToken, keywords, false, false);
				}

		} else if (this.expectedTypesPtr > -1 && this.expectedTypes[0].isAnnotationType()) {
			findTypesAndPackages(this.completionToken, scope, false, false, new ObjectVector());
			if (scope instanceof BlockScope && !this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
				findVariablesAndMethods(
					this.completionToken,
					scope,
					singleNameReference,
					scope,
					insideTypeAnnotation,
					singleNameReference.isInsideAnnotationAttribute);
			}
		} else {
			if (this.expectedTypesPtr > -1) {
				this.assistNodeIsEnum = true;
				done : for (int i = 0; i <= this.expectedTypesPtr; i++) {
					if (!this.expectedTypes[i].isEnum()) {
						this.assistNodeIsEnum = false;
						break done;
					}
				}

			}
			if (scope instanceof BlockScope && !this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
				char[][] alreadyDefinedName = computeAlreadyDefinedName((BlockScope)scope, singleNameReference);

				int end = Math.min(singleNameReference.sourceEnd, this.actualCompletionPosition);
				findUnresolvedReference(
						singleNameReference.sourceStart,
						end,
						(BlockScope)scope,
						alreadyDefinedName);
			}

			checkCancel();

			// see if we can find argument type at position in case if we are at a vararg.
			checkForVarargExpectedTypes(astNodeParent, scope);
			findVariablesAndMethods(this.completionToken, scope, singleNameReference, scope, insideTypeAnnotation,
					singleNameReference.isInsideAnnotationAttribute, true, new ObjectVector());

			checkCancel();

			// can be the start of a qualified type name
			findTypesAndPackages(this.completionToken, scope, true, false, new ObjectVector());
			if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
				if (this.completionToken != null && this.completionToken.length != 0) {
					findKeywords(this.completionToken, singleNameReference.possibleKeywords, false, false);
				} else {
					findTrueOrFalseKeywords(singleNameReference.possibleKeywords);
				}
			}
			if (singleNameReference.canBeExplicitConstructor && !this.requestor.isIgnored(CompletionProposal.METHOD_REF)){
				if (CharOperation.prefixEquals(this.completionToken, Keywords.THIS, false)) {
					ReferenceBinding ref = scope.enclosingSourceType();
					findExplicitConstructors(Keywords.THIS, ref, (MethodScope)scope, singleNameReference);
				} else if (CharOperation.prefixEquals(this.completionToken, Keywords.SUPER, false)) {
					ReferenceBinding ref = scope.enclosingSourceType();
					findExplicitConstructors(Keywords.SUPER, ref.superclass(), (MethodScope)scope, singleNameReference);
				}
			}
		}
	}

	private void checkForVarargExpectedTypes(ASTNode astNodeParent, Scope scope) {
		if (astNodeParent instanceof MessageSend m && this.expectedTypesPtr == -1) {
			final ObjectVector methodsToSearchOn = new ObjectVector();
			final CompletionRequestor actual = this.requestor;
			this.requestor = new CompletionRequestor(true) {

				@Override
				public void accept(CompletionProposal proposal) {
					// do nothing
				}
			};
			this.requestor.setIgnored(CompletionProposal.METHOD_REF, false);
			try {
				// this will help to find the actual resolved method we are at since current MessageSend is not
				// properly resolved. We use the same strategy we have at completionOnMessageSend
				findVariablesAndMethods(m.selector, scope, m, scope, false, false, true, methodsToSearchOn);
			} finally {
				this.requestor = actual;
			}

			pushExpectedTypesForArgumentPosition(methodsToSearchOn,
					(int) Stream.of(m.argumentTypes).filter(Objects::nonNull).count(), scope);
		}
	}

	private void completionOnSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
		CompletionOnSingleTypeReference singleRef = (CompletionOnSingleTypeReference) astNode;

		this.completionToken = singleRef.token;

		this.assistNodeIsClass = singleRef.isClass();
		this.assistNodeIsException = singleRef.isException();
		this.assistNodeIsInterface = singleRef.isInterface();
		this.assistNodeIsConstructor = singleRef.isConstructorType;
		this.assistNodeIsSuperType = singleRef.isSuperType();
		this.assistNodeIsExtendedType = assistNodeIsExtendedType(astNode, astNodeParent);
		this.assistNodeIsInterfaceExcludingAnnotation = assistNodeIsInterfaceExcludingAnnotation(astNode, astNodeParent);

		// can be the start of a qualified type name
		if (qualifiedBinding == null) {
			if (this.completionToken.length == 0 &&
					(astNodeParent instanceof ParameterizedSingleTypeReference ||
							astNodeParent instanceof ParameterizedQualifiedTypeReference)) {
				this.setSourceAndTokenRange(astNode.sourceStart, astNode.sourceStart - 1, false);

				findParameterizedType((TypeReference)astNodeParent, scope);
			} else {
				ObjectVector typesFound = new ObjectVector();
				if (this.assistNodeIsException && astNodeParent instanceof TryStatement) {
					findExceptionFromTryStatement(
							this.completionToken,
							null,
							scope.enclosingSourceType(),
							(BlockScope)scope,
							typesFound);
				}
				else if ( astNodeParent instanceof InstanceOfExpression) {
					// propose final keyword
					if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
						findKeywordsForMember(this.completionToken, (~ClassFileConstants.AccFinal  & 0xFF), astNode);
					}
				}

				checkCancel();

				findTypesAndPackages(this.completionToken, scope, this.assistNodeIsConstructor, false, typesFound);
			}
		} else if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			findMemberTypes(
				this.completionToken,
				(ReferenceBinding) qualifiedBinding,
				scope,
				scope.enclosingSourceType(),
				false,
				false,
				false,
				false,
				!this.assistNodeIsConstructor,
				null,
				new ObjectVector(),
				null,
				null,
				null,
				false);
		}
		ASTNode parentNode = this.parser.assistNodeParent;
		if (parentNode instanceof LocalDeclaration && ((LocalDeclaration) parentNode).type == singleRef) {
			// additionally check if this identifier should be interpreted as the beginning of an expression rather than the type of a variable declaration.
			TypeBinding receiverType = scope.enclosingReceiverType();
			if (receiverType != null && receiverType.isValidBinding()) {
				findVariablesAndMethods(this.completionToken, scope, FakeInvocationSite, scope, false, false);
			}
			// ... or a keyword (possibly starting a new statement):
			if (!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
				if (this.completionToken != null && this.completionToken.length != 0) {
					findKeywords(this.completionToken, singleRef.possibleKeywords, false, false);
				} else {
					findTrueOrFalseKeywords(singleRef.possibleKeywords);
				}
			}
		}
	}

	private void completionOnProvidesInterfacesSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
		completionOnSingleTypeReference(astNode, astNodeParent, qualifiedBinding, scope);
	}
	private void completionOnProvidesImplementationsSingleTypeReference(ASTNode astNode, ASTNode astNodeParent, Binding qualifiedBinding, Scope scope) {
		findImplementations((ProvidesStatement) this.parser.enclosingNode, (TypeReference) astNode);
		// TODO : filter the results - remove packs without a type in impl.
	}

	private char[][] computeAlreadyDefinedName(
			BlockScope scope,
			InvocationSite invocationSite) {
		ArrayList result = new ArrayList();

		boolean staticsOnly = false;

		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;

				//$FALL-THROUGH$
				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 (local.isSecret())
							continue next;

						result.add(local.name);
					}
					break;

				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) currentScope;
					SourceTypeBinding enclosingType = classScope.referenceContext.binding;
					computeAlreadyDefinedName(
							enclosingType,
							classScope,
							staticsOnly,
							invocationSite,
							result);
					staticsOnly |= enclosingType.isStatic();
					break;

				case Scope.COMPILATION_UNIT_SCOPE :
					break done1;
			}
			currentScope = currentScope.parent;
		}

		if (result.size() == 0) return CharOperation.NO_CHAR_CHAR;

		return (char[][])result.toArray(new char[result.size()][]);
	}

	private void computeAlreadyDefinedName(
			FieldBinding[] fields,
			Scope scope,
			boolean onlyStaticFields,
			ReferenceBinding receiverType,
			InvocationSite invocationSite,
			ArrayList result) {

		next : for (int f = fields.length; --f >= 0;) {
			FieldBinding field = fields[f];

			if (field.isSynthetic()) continue next;

			if (onlyStaticFields && !field.isStatic()) continue next;

			if (!field.canBeSeenBy(receiverType, invocationSite, scope)) continue next;

			result.add(field.name);
		}
	}

	private void computeAlreadyDefinedName(
			SourceTypeBinding receiverType,
			ClassScope scope,
			boolean onlyStaticFields,
			InvocationSite invocationSite,
			ArrayList result) {

		ReferenceBinding currentType = receiverType;
		ReferenceBinding[] interfacesToVisit = null;
		int nextPosition = 0;
		do {
			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
				if (interfacesToVisit == null) {
					interfacesToVisit = itsInterfaces;
					nextPosition = interfacesToVisit.length;
				} else {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}

			FieldBinding[] fields = currentType.availableFields();
			if(fields != null && fields.length > 0) {
				computeAlreadyDefinedName(
					fields,
					scope,
					onlyStaticFields,
					receiverType,
					invocationSite,
					result);
			}
			currentType = currentType.superclass();
		} while ( currentType != null);

		if (interfacesToVisit != null) {
			for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding anInterface = interfacesToVisit[i];
				FieldBinding[] fields = anInterface.availableFields();
				if(fields !=  null) {
					computeAlreadyDefinedName(
						fields,
						scope,
						onlyStaticFields,
						receiverType,
						invocationSite,
						result);
				}

				ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
				if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}

	int computeBaseRelevance(){
		return R_DEFAULT;
	}

	private void computeExpectedTypes(ASTNode parent, ASTNode node, Scope scope){

		// default filter
		this.expectedTypesFilter = SUBTYPE;
		this.hasJavaLangObjectAsExpectedType = false;

		// find types from parent
		if(parent instanceof AbstractVariableDeclaration && !(parent instanceof TypeParameter)) {
			AbstractVariableDeclaration variable = (AbstractVariableDeclaration)parent;
			TypeBinding binding = variable.type.resolvedType;
			if(binding != null) {
				if(!(variable.initialization instanceof ArrayInitializer)) {
					addExpectedType(binding, scope);
				} else { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=310747
					// If the variable is of type X[], and we're in the initializer
					// we should have X as the expected type for the variable initializers.
					binding = binding.leafComponentType();
					addExpectedType(binding, scope);
				}
			}
		} else if(parent instanceof Assignment) {
			TypeBinding binding = ((Assignment)parent).lhs.resolvedType;
			if(binding != null) {
				addExpectedType(binding, scope);
			}
		} else if(parent instanceof ReturnStatement) {
			if(scope.methodScope().referenceContext instanceof AbstractMethodDeclaration) {
				MethodBinding methodBinding = ((AbstractMethodDeclaration) scope.methodScope().referenceContext).binding;
				TypeBinding binding = methodBinding  == null ? null : methodBinding.returnType;
				if(binding != null) {
					addExpectedType(binding, scope);
				}
			} else if (scope.methodScope().referenceContext instanceof LambdaExpression) {
				MethodBinding methodBinding = ((LambdaExpression) scope.methodScope().referenceContext).getMethodBinding();
				TypeBinding binding = methodBinding  == null ? null : methodBinding.returnType;
				if (binding != null) {
					addExpectedType(binding, scope);
				}
			}
		} else if (parent instanceof LambdaExpression) {
			LambdaExpression lambda = (LambdaExpression) parent;
			if (lambda.body == node) {
				if (lambda.binding != null) {
					TypeBinding returnType = lambda.binding.returnType;
					if (returnType != null) {
						addExpectedType(returnType, scope);
					}
				}
			}
		} else if(parent instanceof CastExpression) {
			TypeReference e = ((CastExpression)parent).type;
			TypeBinding binding = e.resolvedType;
			if(binding != null){
				addExpectedType(binding, scope);
				this.expectedTypesFilter = SUBTYPE | SUPERTYPE;
			}
		} else if(parent instanceof MessageSend) {
			MessageSend messageSend = (MessageSend) parent;

			if(messageSend.actualReceiverType instanceof ReferenceBinding) {
				ReferenceBinding binding = (ReferenceBinding)messageSend.actualReceiverType;
				boolean isStatic = messageSend.receiver.isTypeReference();

				while(binding != null) {
					computeExpectedTypesForMessageSend(
						binding,
						messageSend.selector,
						messageSend.arguments,
						(ReferenceBinding)messageSend.actualReceiverType,
						scope,
						messageSend,
						isStatic);
					computeExpectedTypesForMessageSendForInterface(
						binding,
						messageSend.selector,
						messageSend.arguments,
						(ReferenceBinding)messageSend.actualReceiverType,
						scope,
						messageSend,
						isStatic);
					binding = binding.superclass();
				}
			}
		} else if(parent instanceof AllocationExpression) {
			AllocationExpression allocationExpression = (AllocationExpression) parent;

			ReferenceBinding binding = (ReferenceBinding) allocationExpression.resolvedType;

			if(binding != null) {
				computeExpectedTypesForAllocationExpression(
					binding,
					allocationExpression.arguments,
					scope,
					allocationExpression);
			}
		} else if(parent instanceof OperatorExpression) {
			int operator = (parent.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT;
			if(parent instanceof ConditionalExpression) {
				// for future use
			} else if(parent instanceof InstanceOfExpression) {
				InstanceOfExpression e = (InstanceOfExpression) parent;
				TypeBinding binding = e.expression.resolvedType;
				if (binding == null) {
					if (scope instanceof BlockScope)
						binding = e.expression.resolveType((BlockScope) scope);
					else if (scope instanceof ClassScope)
						binding = e.expression.resolveType((ClassScope) scope);
				}
				if(binding != null){
					addExpectedType(binding, scope);
					this.expectedTypesFilter = SUBTYPE | SUPERTYPE;
				}
			} else if(parent instanceof BinaryExpression) {
				BinaryExpression binaryExpression = (BinaryExpression) parent;
				switch(operator) {
					case OperatorIds.EQUAL_EQUAL :
					case OperatorIds.NOT_EQUAL :
						// expected type is not relevant in this case
						TypeBinding binding = binaryExpression.left.resolvedType;
						if (binding != null) {
							addExpectedType(binding, scope);
							this.expectedTypesFilter = SUBTYPE | SUPERTYPE;
						}
						break;
					case OperatorIds.PLUS :
						addExpectedType(TypeBinding.SHORT, scope);
						addExpectedType(TypeBinding.INT, scope);
						addExpectedType(TypeBinding.LONG, scope);
						addExpectedType(TypeBinding.FLOAT, scope);
						addExpectedType(TypeBinding.DOUBLE, scope);
						addExpectedType(TypeBinding.CHAR, scope);
						addExpectedType(TypeBinding.BYTE, scope);
						addExpectedType(scope.getJavaLangString(), scope);
						break;
					case OperatorIds.AND_AND :
					case OperatorIds.OR_OR :
					case OperatorIds.XOR :
						addExpectedType(TypeBinding.BOOLEAN, scope);
						break;
					default :
						addExpectedType(TypeBinding.SHORT, scope);
						addExpectedType(TypeBinding.INT, scope);
						addExpectedType(TypeBinding.LONG, scope);
						addExpectedType(TypeBinding.FLOAT, scope);
						addExpectedType(TypeBinding.DOUBLE, scope);
						addExpectedType(TypeBinding.CHAR, scope);
						addExpectedType(TypeBinding.BYTE, scope);
						break;
				}
				if(operator == OperatorIds.LESS) {
					if(binaryExpression.left instanceof SingleNameReference){
						SingleNameReference name = (SingleNameReference) binaryExpression.left;
						Binding b = scope.getBinding(name.token, Binding.VARIABLE | Binding.TYPE, name, false);
						if(b instanceof ReferenceBinding) {
							TypeVariableBinding[] typeVariableBindings =((ReferenceBinding)b).typeVariables();
							if(typeVariableBindings != null && typeVariableBindings.length > 0) {
								addExpectedType(typeVariableBindings[0].firstBound, scope);
							}

						}
					}
				}
			} else if(parent instanceof UnaryExpression) {
				switch(operator) {
					case OperatorIds.NOT :
						addExpectedType(TypeBinding.BOOLEAN, scope);
						break;
					case OperatorIds.TWIDDLE :
						addExpectedType(TypeBinding.SHORT, scope);
						addExpectedType(TypeBinding.INT, scope);
						addExpectedType(TypeBinding.LONG, scope);
						addExpectedType(TypeBinding.CHAR, scope);
						addExpectedType(TypeBinding.BYTE, scope);
						break;
					case OperatorIds.PLUS :
					case OperatorIds.MINUS :
					case OperatorIds.PLUS_PLUS :
					case OperatorIds.MINUS_MINUS :
						addExpectedType(TypeBinding.SHORT, scope);
						addExpectedType(TypeBinding.INT, scope);
						addExpectedType(TypeBinding.LONG, scope);
						addExpectedType(TypeBinding.FLOAT, scope);
						addExpectedType(TypeBinding.DOUBLE, scope);
						addExpectedType(TypeBinding.CHAR, scope);
						addExpectedType(TypeBinding.BYTE, scope);
						break;
				}
			}
		} else if(parent instanceof ArrayReference) {
			addExpectedType(TypeBinding.SHORT, scope);
			addExpectedType(TypeBinding.INT, scope);
			addExpectedType(TypeBinding.LONG, scope);
		} else if(parent instanceof ParameterizedSingleTypeReference) {
			ParameterizedSingleTypeReference ref = (ParameterizedSingleTypeReference) parent;
			TypeBinding expected = null;
			if (this.parser.enclosingNode instanceof AbstractVariableDeclaration ||
					this.parser.enclosingNode instanceof ReturnStatement) {
				// completing inside the diamond
				if (this.parser.enclosingNode instanceof AbstractVariableDeclaration) {
					AbstractVariableDeclaration abstractVariableDeclaration = (AbstractVariableDeclaration) this.parser.enclosingNode;
					expected = abstractVariableDeclaration.initialization != null ? abstractVariableDeclaration.initialization.expectedType() : null;
				} else {
					ReturnStatement returnStatement = (ReturnStatement) this.parser.enclosingNode;
					if (returnStatement.expression != null) {
						expected = returnStatement.expression.expectedType();
					}
				}
				addExpectedType(expected, scope);
			} else {
				TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
				int length = ref.typeArguments == null ? 0 : ref.typeArguments.length;
				if(typeVariables != null && typeVariables.length >= length) {
					int index = length - 1;
					while(index > -1 && ref.typeArguments[index] != node) index--;

					TypeBinding bound = typeVariables[index].firstBound;
					addExpectedType(bound == null ? scope.getJavaLangObject() : bound, scope);
				}
			}
		} else if(parent instanceof ParameterizedQualifiedTypeReference) {
			ParameterizedQualifiedTypeReference ref = (ParameterizedQualifiedTypeReference) parent;
			TypeReference[][] arguments = ref.typeArguments;
			TypeBinding expected = null;
			if (this.parser.enclosingNode instanceof AbstractVariableDeclaration ||
					this.parser.enclosingNode instanceof ReturnStatement) {
				// completing inside the diamond
				if (this.parser.enclosingNode instanceof AbstractVariableDeclaration) {
					AbstractVariableDeclaration abstractVariableDeclaration = (AbstractVariableDeclaration) this.parser.enclosingNode;
					expected = abstractVariableDeclaration.initialization != null ? abstractVariableDeclaration.initialization.expectedType() : null;
				} else {
					ReturnStatement returnStatement = (ReturnStatement) this.parser.enclosingNode;
					if (returnStatement.expression != null) {
						expected = returnStatement.expression.expectedType();
					}
				}
				addExpectedType(expected, scope);
			} else {
				TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
				if(typeVariables != null) {
					int iLength = arguments == null ? 0 : arguments.length;
					done: for (int i = 0; i < iLength; i++) {
						int jLength = arguments[i] == null ? 0 : arguments[i].length;
						for (int j = 0; j < jLength; j++) {
							if(arguments[i][j] == node && typeVariables.length > j) {
								TypeBinding bound = typeVariables[j].firstBound;
								addExpectedType(bound == null ? scope.getJavaLangObject() : bound, scope);
								break done;
							}
						}
					}
				}
			}
		} else if(parent instanceof MemberValuePair) {
			MemberValuePair memberValuePair = (MemberValuePair) parent;
			if(memberValuePair.binding != null) {
				addExpectedType(memberValuePair.binding.returnType.leafComponentType(), scope);
			}
		} else if (parent instanceof NormalAnnotation) {
			NormalAnnotation annotation = (NormalAnnotation) parent;
			MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
			if(memberValuePairs == null || memberValuePairs.length == 0) {
				if(annotation.resolvedType instanceof ReferenceBinding) {
					MethodBinding[] methodBindings =
						((ReferenceBinding)annotation.resolvedType).availableMethods();
					if (methodBindings != null &&
							methodBindings.length > 0 &&
							CharOperation.equals(methodBindings[0].selector, VALUE)) {
						boolean canBeSingleMemberAnnotation = true;
						done : for (int i = 1; i < methodBindings.length; i++) {
							if((methodBindings[i].modifiers & ClassFileConstants.AccAnnotationDefault) == 0) {
								canBeSingleMemberAnnotation = false;
								break done;
							}
						}
						if (canBeSingleMemberAnnotation) {
							this.assistNodeCanBeSingleMemberAnnotation = canBeSingleMemberAnnotation;
							addExpectedType(methodBindings[0].returnType.leafComponentType(), scope);
						}
					}
				}
			}
		} else if (parent instanceof AssistNodeParentAnnotationArrayInitializer) {
			AssistNodeParentAnnotationArrayInitializer parent1 = (AssistNodeParentAnnotationArrayInitializer) parent;
			if(parent1.type.resolvedType instanceof ReferenceBinding) {
				MethodBinding[] methodBindings =
					((ReferenceBinding)parent1.type.resolvedType).availableMethods();
				if (methodBindings != null) {
					for (MethodBinding methodBinding : methodBindings) {
						if(CharOperation.equals(methodBinding.selector, parent1.name)) {
							addExpectedType(methodBinding.returnType.leafComponentType(), scope);
							break;
						}
					}
				}
			}
		} else if (parent instanceof TryStatement) {
			boolean isException = false;
			if (node instanceof CompletionOnSingleTypeReference) {
				isException = ((CompletionOnSingleTypeReference)node).isException();
			} else if (node instanceof CompletionOnQualifiedTypeReference) {
				isException = ((CompletionOnQualifiedTypeReference)node).isException();
			} else if (node instanceof CompletionOnParameterizedQualifiedTypeReference) {
				isException = ((CompletionOnParameterizedQualifiedTypeReference)node).isException();
			}
			if (isException) {
				ThrownExceptionFinder thrownExceptionFinder = new ThrownExceptionFinder();
				thrownExceptionFinder.processThrownExceptions((TryStatement) parent, (BlockScope)scope);
				ReferenceBinding[] bindings = thrownExceptionFinder.getThrownUncaughtExceptions();
				ReferenceBinding[] alreadyCaughtExceptions = thrownExceptionFinder.getAlreadyCaughtExceptions();
				ReferenceBinding[] discouragedExceptions = thrownExceptionFinder.getDiscouragedExceptions();
				if (bindings != null && bindings.length > 0) {
					for (int i = 0; i < bindings.length; i++) {
						addExpectedType(bindings[i], scope);
					}
					this.expectedTypesFilter = SUPERTYPE;
				}
				if (alreadyCaughtExceptions != null && alreadyCaughtExceptions.length > 0) {
					for (int i = 0; i < alreadyCaughtExceptions.length; i++) {
						addForbiddenBindings(alreadyCaughtExceptions[i]);
						this.knownTypes.put(CharOperation.concat(alreadyCaughtExceptions[i].qualifiedPackageName(), alreadyCaughtExceptions[i].qualifiedSourceName(), '.'), KNOWN_TYPE_WITH_KNOWN_CONSTRUCTORS);
					}
				}
				if (discouragedExceptions != null && discouragedExceptions.length > 0) {
					for (int i = 0; i < discouragedExceptions.length; i++) {
						addUninterestingBindings(discouragedExceptions[i]);
						// do not insert into known types. We do need these types to come from
						// searchAllTypes(..) albeit with lower relevance
					}
				}
			}
		} else if (parent instanceof SwitchStatement) {
			SwitchStatement switchStatement = (SwitchStatement) parent;
			this.assistNodeIsInsideCase = assistNodeIsInsideCase(node, parent);
			if (switchStatement.expression != null &&
					switchStatement.expression.resolvedType != null) {
				if (this.assistNodeIsInsideCase &&
						switchStatement.expression.resolvedType.id == TypeIds.T_JavaLangString &&
						this.compilerOptions.complianceLevel >= ClassFileConstants.JDK1_7) {
					// set the field to true even though the expected types array will contain String as
					// expected type to avoid traversing the array in every case later on.
					// https://bugs.eclipse.org/bugs/show_bug.cgi?id=343476
					this.assistNodeIsString = true;
				}
				addExpectedType(switchStatement.expression.resolvedType, scope);
			}
		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=253008, flag boolean as the expected
		// type if we are completing inside if(), for (; ;), while() and do while()
		} else if (parent instanceof WhileStatement) {  // covers both while and do-while loops
			addExpectedType(TypeBinding.BOOLEAN, scope);
		} else if (parent instanceof IfStatement) {
			addExpectedType(TypeBinding.BOOLEAN, scope);
		} else if (parent instanceof AssertStatement) {
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=274466
			// If the assertExpression is same as the node , then the assistNode is the conditional part of the assert statement
			AssertStatement assertStatement = (AssertStatement) parent;
			if (assertStatement.assertExpression == node) {
				addExpectedType(TypeBinding.BOOLEAN, scope);
			}
		} else if (parent instanceof ForStatement) {   // astNodeParent set to ForStatement only for the condition
			addExpectedType(TypeBinding.BOOLEAN, scope);

		// Expected types for javadoc
		} else if (parent instanceof Javadoc) {
			if (scope.kind == Scope.METHOD_SCOPE) {
				MethodScope methodScope = (MethodScope) scope;
				AbstractMethodDeclaration methodDecl = methodScope.referenceMethod();
				if (methodDecl != null && methodDecl.binding != null) {
					ReferenceBinding[] exceptions = methodDecl.binding.thrownExceptions;
					if (exceptions != null) {
						for (int i = 0; i < exceptions.length; i++) {
							addExpectedType(exceptions[i], scope);
						}
					}
				}
			}
		}

		// Guard it, otherwise we end up with a empty array which cause issues down the line
		if((this.expectedTypesPtr > -1) && ((this.expectedTypesPtr + 1) != this.expectedTypes.length)) {
			System.arraycopy(this.expectedTypes, 0, this.expectedTypes = new TypeBinding[this.expectedTypesPtr + 1], 0, this.expectedTypesPtr + 1);
		}
	}

	private void computeExpectedTypesForAllocationExpression(
		ReferenceBinding binding,
		Expression[] arguments,
		Scope scope,
		InvocationSite invocationSite) {

		if (arguments == null)
			return;

		MethodBinding[] methods = binding.availableMethods();
		nextMethod : for (int i = 0; i < methods.length; i++) {
			MethodBinding method = methods[i];

			if (!method.isConstructor()) continue nextMethod;

			if (method.isSynthetic()) continue nextMethod;

			if (this.options.checkVisibility && !method.canBeSeenBy(invocationSite, scope)) continue nextMethod;

			TypeBinding[] parameters = method.parameters;
			if(parameters.length < arguments.length)
				continue nextMethod;

			int length = arguments.length - 1;

			for (int j = 0; j < length; j++) {
				Expression argument = arguments[j];
				TypeBinding argType = argument.resolvedType;
				if(argType != null && !argType.isCompatibleWith(parameters[j]))
					continue nextMethod;
			}

			TypeBinding expectedType = method.parameters[arguments.length - 1];
			if(expectedType != null) {
				addExpectedType(expectedType, scope);
			}
		}
	}
	private void computeExpectedTypesForMessageSend(
		ReferenceBinding binding,
		char[] selector,
		Expression[] arguments,
		ReferenceBinding receiverType,
		Scope scope,
		InvocationSite invocationSite,
		boolean isStatic) {

		if (arguments == null)
			return;

		MethodBinding[] methods = binding.availableMethods();
		nextMethod : for (int i = 0; i < methods.length; i++) {
			MethodBinding method = methods[i];

			if (method.isSynthetic()) continue nextMethod;

			if (method.isDefaultAbstract())	continue nextMethod;

			if (method.isConstructor()) continue nextMethod;

			if (isStatic && !method.isStatic()) continue nextMethod;

			if (this.options.checkVisibility && !method.canBeSeenBy(receiverType, invocationSite, scope)) continue nextMethod;

			if(!CharOperation.equals(method.selector, selector)) continue nextMethod;

			TypeBinding[] parameters = method.parameters;
			if(parameters.length < arguments.length)
				continue nextMethod;

			int length = arguments.length - 1;
			int completionArgIndex = arguments.length - 1;

			for (int j = 0; j < length; j++) {
				Expression argument = arguments[j];
				TypeBinding argType = argument.resolvedType;
				if(argType != null && !argType.erasure().isCompatibleWith(parameters[j].erasure()))
					continue nextMethod;

				if((argument.sourceStart >= this.startPosition)
						&& (argument.sourceEnd <= this.endPosition)) {
					completionArgIndex = j;
				}
			}

			TypeBinding expectedType = method.parameters[completionArgIndex];
			if(expectedType != null) {
				addExpectedType(expectedType, scope);
			}
		}
	}
	private void computeExpectedTypesForMessageSendForInterface(
		ReferenceBinding binding,
		char[] selector,
		Expression[] arguments,
		ReferenceBinding receiverType,
		Scope scope,
		InvocationSite invocationSite,
		boolean isStatic) {

		ReferenceBinding[] itsInterfaces = binding.superInterfaces();
		if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
			ReferenceBinding[] interfacesToVisit = itsInterfaces;
			int nextPosition = interfacesToVisit.length;

			for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding currentType = interfacesToVisit[i];
				computeExpectedTypesForMessageSend(
					currentType,
					selector,
					arguments,
					receiverType,
					scope,
					invocationSite,
					isStatic);

				if ((itsInterfaces = currentType.superInterfaces()) != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}

	private Scope computeForbiddenBindings(ASTNode astNode, ASTNode astNodeParent, Scope scope) {
		if(scope instanceof ClassScope) {
			TypeDeclaration typeDeclaration = ((ClassScope)scope).referenceContext;
			if(typeDeclaration.superclass == astNode) {
				addForbiddenBindings(typeDeclaration.binding);
				addForbiddenBindingsForMemberTypes(typeDeclaration);
				return scope.parent;
			}
			TypeReference[] superInterfaces = typeDeclaration.superInterfaces;
			int length = superInterfaces == null ? 0 : superInterfaces.length;
			int astNodeIndex = -1;
			for (int i = 0; i < length; i++) {
				if(superInterfaces[i] == astNode) {
					addForbiddenBindings(typeDeclaration.binding);
					addForbiddenBindingsForMemberTypes(typeDeclaration);
					astNodeIndex = i;
					break;
				}
			}
			if (astNodeIndex >= 0) {
				// Need to loop only up to astNodeIndex as the rest will be undefined.
				for (int i = 0; i < astNodeIndex; i++) {
					addForbiddenBindings(superInterfaces[i].resolvedType);
				}
				return scope.parent;
			}
		}
//		else if(scope instanceof MethodScope) {
//			MethodScope methodScope = (MethodScope) scope;
//			if(methodScope.insideTypeAnnotation) {
//				return methodScope.parent.parent;
//			}
//		}
		return scope;
	}

	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=270437
	private void addForbiddenBindingsForMemberTypes(TypeDeclaration typeDeclaration) {
		TypeDeclaration[] memberTypes = typeDeclaration.memberTypes;
		int memberTypesLen = memberTypes == null ? 0 : memberTypes.length;
		for (int i = 0; i < memberTypesLen; i++) {
			addForbiddenBindings(memberTypes[i].binding);
			addForbiddenBindingsForMemberTypes(memberTypes[i]);
		}
	}

	private char[] computePrefix(SourceTypeBinding declarationType, SourceTypeBinding invocationType, boolean isStatic){

		StringBuilder completion = new StringBuilder(10);

		if (isStatic) {
			completion.append(declarationType.sourceName());

		} else if (TypeBinding.equalsEquals(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 int computeRelevanceForAnnotation(){
		if(this.assistNodeIsAnnotation) {
			return R_ANNOTATION;
		}
		return 0;
	}

	private int computeRelevanceForAnnotationTarget(TypeBinding typeBinding){
		if (this.assistNodeIsAnnotation &&
				(this.targetedElement & TagBits.AnnotationTargetMASK) != 0) {
			long target = typeBinding.getAnnotationTagBits() & TagBits.AnnotationTargetMASK;
			if(target == 0 || (target & this.targetedElement) != 0) {
				return R_TARGET;
			}
		}
		return 0;
	}

	int computeRelevanceForCaseMatching(char[][] tokens, char[] proposalName) {
		int finalRelevance = 0;
		for (int i = 0; i < tokens.length; i++) {
			int relevance = computeRelevanceForCaseMatching(tokens[i], proposalName);
			if (relevance > finalRelevance) {
				finalRelevance = relevance;
			}
		}
		return finalRelevance;
	}

	int computeRelevanceForCaseMatching(char[] token, char[] proposalName) {
		if (CharOperation.equals(token, proposalName, true)) {
			return R_EXACT_NAME + R_CASE;
		} else if (CharOperation.equals(token, proposalName, false)) {
			return R_EXACT_NAME;
		} else if (CharOperation.prefixEquals(token, proposalName, false)) {
			if (CharOperation.prefixEquals(token, proposalName, true))
				return R_CASE;
		} else if (this.options.camelCaseMatch && CharOperation.camelCaseMatch(token, proposalName)) {
			return R_CAMEL_CASE;
		} else if (this.options.substringMatch && CharOperation.substringMatch(token, proposalName)) {
			return R_SUBSTRING;
		} else if (this.options.subwordMatch && CharOperation.subWordMatch(token, proposalName)) {
			return R_SUBWORD;
		}
		return 0;
	}

	private int computeRelevanceForClass(){
		if(this.assistNodeIsClass) {
			return R_CLASS;
		}
		return 0;
	}

	private int computeRelevanceForConstructor() {
		if (this.assistNodeIsConstructor) {
			return R_CONSTRUCTOR;
		}
		return 0;
	}

	private int computeRelevanceForEnum(){
		if(this.assistNodeIsEnum) {
			return R_ENUM;
		}
		return 0;
	}

	private int computeRelevanceForEnumConstant(TypeBinding proposalType){
		if(this.assistNodeIsEnum &&
				proposalType != null &&
				this.expectedTypes != null) {
			for (int i = 0; i <= this.expectedTypesPtr; i++) {
				if (proposalType.isEnum() &&
						TypeBinding.equalsEquals(proposalType, this.expectedTypes[i])) {
					return R_ENUM + R_ENUM_CONSTANT;
				}

			}
		}
		return 0;
	}

	private int computeRelevanceForException(){
		if (this.assistNodeIsException) {
			return R_EXCEPTION;
		}
		return 0;
	}

	private int computeRelevanceForException(char[] proposalName){

		if((this.assistNodeIsException || (this.assistNodeInJavadoc & CompletionOnJavadoc.EXCEPTION) != 0 )&&
			(CharOperation.match(EXCEPTION_PATTERN, proposalName, false) ||
			CharOperation.match(ERROR_PATTERN, proposalName, false))) {
			return R_EXCEPTION;
		}
		return 0;
	}

	private int computeRelevanceForExpectingType(char[] packageName, char[] typeName){
		if(this.expectedTypes != null) {
			for (int i = 0; i <= this.expectedTypesPtr; i++) {
				if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), packageName) &&
					CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), typeName)) {
					return R_EXACT_EXPECTED_TYPE;
				}
			}
			if(this.hasJavaLangObjectAsExpectedType) {
				return R_EXPECTED_TYPE;
			}
		}
		return 0;
	}

	private int computeRelevanceForExpectingType(TypeBinding proposalType){
		if(this.expectedTypes != null && proposalType != null) {
			int relevance = 0;
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=271296
			// If there is at least one expected type, then void proposal types attract a degraded relevance.
			if (proposalType == TypeBinding.VOID && this.expectedTypesPtr >=0) {
				return R_VOID;
			}
			for (int i = 0; i <= this.expectedTypesPtr; i++) {
				if((this.expectedTypesFilter & SUBTYPE) != 0
						&& (proposalType.erasure().isCompatibleWith(this.expectedTypes[i].erasure()))) {

					if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), proposalType.qualifiedPackageName()) ) {
						if(CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName()))
								return R_EXACT_EXPECTED_TYPE;
						if(!CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName()) &&
								proposalType.qualifiedPackageName().length !=0)
							return R_PACKAGE_EXPECTED_TYPE;
					}

					relevance = R_EXPECTED_TYPE;

				}
				if((this.expectedTypesFilter & SUPERTYPE) != 0
						&& this.expectedTypes[i].isCompatibleWith(proposalType)) {

					if(CharOperation.equals(this.expectedTypes[i].qualifiedPackageName(), proposalType.qualifiedPackageName()) &&
							CharOperation.equals(this.expectedTypes[i].qualifiedSourceName(), proposalType.qualifiedSourceName())) {
						return R_EXACT_EXPECTED_TYPE;
					}

					relevance = R_EXPECTED_TYPE;
				}
				// Bug 84720 - [1.5][assist] proposal ranking by return value should consider auto(un)boxing
				// Just ensuring that the unitScope is not null, even though it's an unlikely case.
				if (this.unitScope != null && this.unitScope.isBoxingCompatibleWith(proposalType, this.expectedTypes[i])) {
					relevance = R_EXPECTED_TYPE;
				}
			}
			return relevance;
		}
		return 0;
	}

	private int computeRelevanceForInheritance(ReferenceBinding receiverType, ReferenceBinding declaringClass) {
		if (TypeBinding.equalsEquals(receiverType, declaringClass)) return R_NON_INHERITED;
		return 0;
	}

	int computeRelevanceForInterestingProposal(){
		return computeRelevanceForInterestingProposal(null);
	}

	private int computeRelevanceForInterestingProposal(Binding binding){
		if(this.uninterestingBindings != null) {
			for (int i = 0; i <= this.uninterestingBindingsPtr; i++) {
				if(this.uninterestingBindings[i] == binding) {
					return 0;
				}
				if((this.uninterestingBindingsFilter & SUBTYPE) != 0) {
					if (binding instanceof TypeBinding &&
							this.uninterestingBindings[i] instanceof TypeBinding &&
							((TypeBinding)binding).isCompatibleWith((TypeBinding)this.uninterestingBindings[i])) {
						return 0;
					}
				}
				if ((this.uninterestingBindingsFilter & SUPERTYPE) != 0) {
					if (binding instanceof TypeBinding &&
							this.uninterestingBindings[i] instanceof TypeBinding &&
							((TypeBinding)this.uninterestingBindings[i]).isCompatibleWith((TypeBinding)binding)) {
						return 0;
					}
				}
			}
		}
		return R_INTERESTING;
	}

	private int computeRelevanceForInterestingProposal(char[] givenPkgName, char[] fullTypeName) {
		for (int i = 0; i <= this.uninterestingBindingsPtr; i++) {
			if (this.uninterestingBindings[i] instanceof TypeBinding) {
				TypeBinding typeBinding = (TypeBinding) this.uninterestingBindings[i];
				char[] currPkgName = typeBinding.qualifiedPackageName();
				if (CharOperation.equals(givenPkgName, currPkgName))	{
					char[] currTypeName = typeBinding.qualifiedSourceName();
					if (CharOperation.equals(fullTypeName, currTypeName)) {
						return 0;
					}
				}
			}
		}
		return R_INTERESTING;
	}

	private int computeRelevanceForInterface(){
		if(this.assistNodeIsInterface) {
			return R_INTERFACE;
		}
		return 0;
	}

	private int computeRelevanceForMissingElements(boolean hasProblems) {
		if (!hasProblems) {
			return R_NO_PROBLEMS;
		}
		return 0;
	}
	int computeRelevanceForQualification(boolean prefixRequired) {
		if(!prefixRequired && !this.insideQualifiedReference) {
			return R_UNQUALIFIED;
		}

		if(prefixRequired && this.insideQualifiedReference) {
			return R_QUALIFIED;
		}
		return 0;
	}

	int computeRelevanceForResolution(){
		return computeRelevanceForResolution(true);
	}

	int computeRelevanceForResolution(boolean isResolved){
		if (isResolved) {
			return R_RESOLVED;
		}
		return 0;
	}

	int computeRelevanceForRestrictions(int accessRuleKind) {
		if(accessRuleKind == IAccessRule.K_ACCESSIBLE) {
			return R_NON_RESTRICTED;
		}
		return 0;
	}

	private int computeRelevanceForStatic(boolean onlyStatic, boolean isStatic) {
		if(this.insideQualifiedReference && !onlyStatic && !isStatic) {
			return R_NON_STATIC;
		}
		return 0;
	}

	private int computeRelevanceForFinal(boolean onlyFinal, boolean isFinal) {
		if (onlyFinal && isFinal) {
			return R_FINAL;
		}
		return 0;
	}

	private int computeRelevanceForSuper(MethodBinding method, Scope scope, InvocationSite site) {
		if (site instanceof CompletionOnMemberAccess) {
			CompletionOnMemberAccess access = (CompletionOnMemberAccess) site;
			if (access.isSuperAccess() && this.parser.assistNodeParent == null) {
				ReferenceContext referenceContext = scope.referenceContext();
				if (referenceContext instanceof AbstractMethodDeclaration) {  // LE is anonymous.
					MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
					if (binding != null) {
						if (CharOperation.equals(binding.selector, method.selector)) {
							if (binding.areParameterErasuresEqual(method)) {
								return R_EXACT_NAME + R_METHOD_OVERIDE;
							}
							return R_EXACT_NAME;
						}
					}
				}
			}
		}
		return 0;
	}

	private long computeTargetedElement(CompletionOnAnnotationOfType fakeNode) {
		ASTNode annotatedElement = fakeNode.potentialAnnotatedNode;

		if (annotatedElement instanceof TypeDeclaration) {
			TypeDeclaration annotatedTypeDeclaration = (TypeDeclaration) annotatedElement;
			if (TypeDeclaration.kind(annotatedTypeDeclaration.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL) {
				return TagBits.AnnotationForAnnotationType | TagBits.AnnotationForType | TagBits.AnnotationForTypeUse;
			}
			return TagBits.AnnotationForType | TagBits.AnnotationForTypeUse;
		} else if (annotatedElement instanceof FieldDeclaration) {
			if (fakeNode.isParameter) {
				return TagBits.AnnotationForParameter;
			}
			return TagBits.AnnotationForField;
		} else if (annotatedElement instanceof MethodDeclaration) {
			return TagBits.AnnotationForMethod;
		} else if (annotatedElement instanceof Argument) {
			return TagBits.AnnotationForParameter;
		} else if (annotatedElement instanceof ConstructorDeclaration) {
			return TagBits.AnnotationForConstructor;
		} else if (annotatedElement instanceof LocalDeclaration) {
			return TagBits.AnnotationForLocalVariable;
		} else if (annotatedElement instanceof ImportReference) {
			return TagBits.AnnotationForPackage;
		}
		return 0;
	}
	private TypeBinding[] computeTypes(Expression[] arguments) {
		if (arguments == null) return null;
		int argsLength = arguments.length;
		TypeBinding[] argTypes = new TypeBinding[argsLength];
		for (int a = argsLength; --a >= 0;) {
			argTypes[a] = arguments[a].resolvedType;
		}
		return argTypes;
	}

	private TypeBinding[] computeTypesIfCorrect(Expression[] arguments) {
		if (arguments == null) return Binding.NO_TYPES;
		int argsLength = arguments.length;
		TypeBinding[] argTypes = new TypeBinding[argsLength];
		for (int a = argsLength; --a >= 0;) {
			TypeBinding typeBinding = arguments[a].resolvedType;
			if(typeBinding == null || !typeBinding.isValidBinding()) return null;
			argTypes[a] = typeBinding;
		}
		return argTypes;
	}

	private void computeUninterestingBindings(ASTNode astNode, ASTNode parent, Scope scope){
		this.uninterestingBindingsFilter = NONE;
		if(parent instanceof LocalDeclaration) {
			addUninterestingBindings(((LocalDeclaration)parent).binding);
		} else if (parent instanceof FieldDeclaration) {
			addUninterestingBindings(((FieldDeclaration)parent).binding);
		} else if (parent instanceof TryStatement) {
			boolean isException = false;
			if (astNode instanceof CompletionOnSingleTypeReference) {
				isException = ((CompletionOnSingleTypeReference)astNode).isException();
			} else if (astNode instanceof CompletionOnQualifiedTypeReference) {
				isException = ((CompletionOnQualifiedTypeReference)astNode).isException();
			} else if (astNode instanceof CompletionOnParameterizedQualifiedTypeReference) {
				isException = ((CompletionOnParameterizedQualifiedTypeReference)astNode).isException();
			}
			if (isException) {
				this.uninterestingBindingsFilter |= SUBTYPE;
				// super-types also need to be discouraged if we're in a union type (bug 350652)
				Argument[] args = ((TryStatement)parent).catchArguments;
				for (int i = 0; i < args.length; i++) {
					if (args[i].type instanceof UnionTypeReference) {
						CompletionNodeDetector detector = new CompletionNodeDetector(astNode, args[i]);
						if (detector.containsCompletionNode()) {
							this.uninterestingBindingsFilter |= SUPERTYPE;
							break;
						}
					}
				}

			}
		}
	}

	private char[] createImportCharArray(char[] importedElement, boolean isStatic, boolean onDemand) {
		char[] result = IMPORT;
		if (isStatic) {
			result = CharOperation.concat(result, STATIC, ' ');
		}
		result = CharOperation.concat(result, importedElement, ' ');
		if (onDemand) {
			result = CharOperation.concat(result, ON_DEMAND);
		}
		return CharOperation.concat(result, IMPORT_END);
	}

	private void createMethod(MethodBinding method, char[][] parameterPackageNames, char[][] parameterTypeNames, char[][] parameterNames, Scope scope, StringBuffer completion) {
		//// Modifiers
		// flush uninteresting modifiers
		int insertedModifiers = method.modifiers & ~(ClassFileConstants.AccNative | ClassFileConstants.AccAbstract);
		if(insertedModifiers != ClassFileConstants.AccDefault){
			ASTNode.printModifiers(insertedModifiers, completion);
		}

		//// Type parameters

		TypeVariableBinding[] typeVariableBindings = method.typeVariables;
		if(typeVariableBindings != null && typeVariableBindings.length != 0) {
			completion.append('<');
			for (int i = 0; i < typeVariableBindings.length; i++) {
				if(i != 0) {
					completion.append(',');
					completion.append(' ');
				}
				createTypeVariable(typeVariableBindings[i], scope, completion);
			}
			completion.append('>');
			completion.append(' ');
		}

		//// Return type
		createType(method.returnType, scope, completion);
		completion.append(' ');

		//// Selector
		completion.append(method.selector);

		completion.append('(');

		////Parameters
		TypeBinding[] parameterTypes = method.parameters;
		int length = parameterTypes.length;
		for (int i = 0; i < length; i++) {
			if(i != 0) {
				completion.append(',');
				completion.append(' ');
			}
			createType(parameterTypes[i], scope, completion);
			completion.append(' ');
			if(parameterNames != null){
				completion.append(parameterNames[i]);
			} else {
				completion.append('%');
			}
		}

		completion.append(')');

		//// Exceptions
		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++){
				if(i != 0) {
					completion.append(' ');
					completion.append(',');
				}
				createType(exceptions[i], scope, completion);
			}
		}
	}

	protected InternalCompletionProposal createProposal(int kind, int completionOffset) {
		InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(kind, completionOffset - this.offset);
		proposal.nameLookup = this.nameEnvironment.nameLookup;
		proposal.completionEngine = this;
		return proposal;
	}

	private CompletionProposal createRequiredTypeProposal(Binding binding, int start, int end, int relevance) {
		InternalCompletionProposal proposal = null;
		if (binding instanceof ReferenceBinding) {
			ReferenceBinding typeBinding = (ReferenceBinding) binding;

			char[] packageName = typeBinding.qualifiedPackageName();
			char[] typeName = typeBinding.qualifiedSourceName();
			char[] fullyQualifiedName = CharOperation.concat(packageName, typeName, '.');

			proposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setDeclarationSignature(packageName);
			proposal.setSignature(getRequiredTypeSignature(typeBinding));
			proposal.setPackageName(packageName);
			proposal.setTypeName(typeName);
			proposal.setCompletion(fullyQualifiedName);
			proposal.setFlags(typeBinding.modifiers);
			proposal.setReplaceRange(start - this.offset, end - this.offset);
			proposal.setTokenRange(start - this.offset, end - this.offset);
			proposal.setRelevance(relevance);
		} else if (binding instanceof PackageBinding) {
			PackageBinding packageBinding = (PackageBinding) binding;

			char[] packageName = CharOperation.concatWith(packageBinding.compoundName, '.');

			proposal = createProposal(CompletionProposal.PACKAGE_REF, this.actualCompletionPosition);
			proposal.setDeclarationSignature(packageName);
			proposal.setPackageName(packageName);
			proposal.setCompletion(packageName);
			proposal.setReplaceRange(start - this.offset, end - this.offset);
			proposal.setTokenRange(start - this.offset, end - this.offset);
			proposal.setRelevance(relevance);
		}
		return proposal;
	}

	private void createType(TypeBinding type, Scope scope, StringBuffer completion) {
		switch (type.kind()) {
			case Binding.BASE_TYPE :
				completion.append(type.sourceName());
				break;
			case Binding.WILDCARD_TYPE :
			case Binding.INTERSECTION_TYPE : // TODO (david) need to handle intersection type specifically
				WildcardBinding wildcardBinding = (WildcardBinding) type;
				completion.append('?');
				switch (wildcardBinding.boundKind) {
					case Wildcard.EXTENDS:
						completion.append(' ');
						completion.append(EXTENDS);
						completion.append(' ');
						createType(wildcardBinding.bound, scope, completion);
						if(wildcardBinding.otherBounds != null) {

							int length = wildcardBinding.otherBounds.length;
							for (int i = 0; i < length; i++) {
								completion.append(' ');
								completion.append('&');
								completion.append(' ');
								createType(wildcardBinding.otherBounds[i], scope, completion);
							}
						}
						break;
					case Wildcard.SUPER:
						completion.append(' ');
						completion.append(SUPER);
						completion.append(' ');
						createType(wildcardBinding.bound, scope, completion);
						break;
				}
				break;
			case Binding.ARRAY_TYPE :
				createType(type.leafComponentType(), scope, completion);
				int dim = type.dimensions();
				for (int i = 0; i < dim; i++) {
					completion.append('[');
					completion.append(']');
				}
				break;
			case Binding.PARAMETERIZED_TYPE :
				ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) type;
				if (type.isMemberType()) {
					createType(parameterizedType.enclosingType(), scope, completion);
					completion.append('.');
					completion.append(parameterizedType.sourceName);
				} else {
					completion.append(CharOperation.concatWith(parameterizedType.genericType().compoundName, '.'));
				}
				if (parameterizedType.arguments != null) {
					completion.append('<');
				    for (int i = 0, length = parameterizedType.arguments.length; i < length; i++) {
				        if (i != 0) completion.append(',');
				        createType(parameterizedType.arguments[i], scope, completion);
				    }
				    completion.append('>');
				}
				break;
			default :
				char[] packageName = type.qualifiedPackageName();
			char[] typeName = type.qualifiedSourceName();
			if(mustQualifyType(
					(ReferenceBinding)type,
					packageName,
					scope)) {
				completion.append(CharOperation.concat(packageName, typeName,'.'));
			} else {
				completion.append(type.sourceName());
			}
			break;
		}
	}

	/*
	 * Create a completion proposal for a member type.
	 */
	private void createTypeParameterProposal(TypeParameter typeParameter, int relevance) {
		char[] completionName = typeParameter.name;

		// Create standard type proposal
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(CompletionProposal.TYPE_REF, this.actualCompletionPosition - this.offset);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setSignature(getSignature(typeParameter.binding));
			proposal.setTypeName(completionName);
			proposal.setCompletion(completionName);
			proposal.setFlags(typeParameter.modifiers);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}

		// Create javadoc text proposal if necessary
		if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_TYPE_REF)) {
			char[] javadocCompletion= inlineTagCompletion(completionName, JavadocTagConstants.TAG_LINK);
			InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(CompletionProposal.JAVADOC_TYPE_REF, this.actualCompletionPosition - this.offset);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setSignature(getSignature(typeParameter.binding));
			proposal.setTypeName(javadocCompletion);
			proposal.setCompletion(javadocCompletion);
			proposal.setFlags(typeParameter.modifiers);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance+R_INLINE_TAG);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}

	/*
	 * Create a completion proposal for a type.
	 */
	private void createTypeProposal(char[] packageName, char[] typeName, int modifiers, int accessibility, char[] completionName, int relevance) {

		// Create standard type proposal
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
			InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(CompletionProposal.TYPE_REF, this.actualCompletionPosition - this.offset);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setDeclarationSignature(packageName);
			proposal.setSignature(createNonGenericTypeSignature(packageName, typeName));
			proposal.setPackageName(packageName);
			proposal.setTypeName(typeName);
			proposal.setCompletion(completionName);
			proposal.setFlags(modifiers);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			proposal.setAccessibility(accessibility);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}

		// Create javadoc text proposal if necessary
		if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_TYPE_REF)) {
			char[] javadocCompletion= inlineTagCompletion(completionName, JavadocTagConstants.TAG_LINK);
			InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(CompletionProposal.JAVADOC_TYPE_REF, this.actualCompletionPosition - this.offset);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setDeclarationSignature(packageName);
			proposal.setSignature(createNonGenericTypeSignature(packageName, typeName));
			proposal.setPackageName(packageName);
			proposal.setTypeName(typeName);
			proposal.setCompletion(javadocCompletion);
			proposal.setFlags(modifiers);
			int start = (this.assistNodeInJavadoc & CompletionOnJavadoc.REPLACE_TAG) != 0 ? this.javadocTagPosition : this.startPosition;
			proposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance+R_INLINE_TAG);
			proposal.setAccessibility(accessibility);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}

	/*
	 * Create a completion proposal for a member type.
	 */
	private void createTypeProposal(
			ReferenceBinding refBinding,
			char[] typeName,
			int accessibility,
			char[] completionName,
			int relevance,
			Binding[] missingElements,
			int[] missingElementsStarts,
			int[] missingElementsEnds,
			boolean missingElementsHaveProblems) {

		// Create standard type proposal
		if(!this.isIgnored(CompletionProposal.TYPE_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
			InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(CompletionProposal.TYPE_REF, this.actualCompletionPosition - this.offset);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setDeclarationSignature(refBinding.qualifiedPackageName());
			proposal.setSignature(getCompletedTypeSignature(refBinding));
			if (proposal.getSignature() == null) {
				return;
			}
			proposal.setPackageName(refBinding.qualifiedPackageName());
			proposal.setTypeName(typeName);
			if (missingElements != null) {
				CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
				for (int i = 0; i < missingElements.length; i++) {
					subProposals[i] =
						createRequiredTypeProposal(
								missingElements[i],
								missingElementsStarts[i],
								missingElementsEnds[i],
								relevance);
				}
				proposal.setRequiredProposals(subProposals);
			}
			proposal.setCompletion(completionName);
			proposal.setFlags(refBinding.modifiers);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}

		// Create javadoc text proposal if necessary
		if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_TYPE_REF)) {
			char[] javadocCompletion= inlineTagCompletion(completionName, JavadocTagConstants.TAG_LINK);
			InternalCompletionProposal proposal = (InternalCompletionProposal) CompletionProposal.create(CompletionProposal.JAVADOC_TYPE_REF, this.actualCompletionPosition - this.offset);
			proposal.nameLookup = this.nameEnvironment.nameLookup;
			proposal.completionEngine = this;
			proposal.setDeclarationSignature(refBinding.qualifiedPackageName());
			proposal.setSignature(getCompletedTypeSignature(refBinding));
			if (proposal.getSignature() == null) {
				return;
			}
			proposal.setPackageName(refBinding.qualifiedPackageName());
			proposal.setTypeName(typeName);
			proposal.setCompletion(javadocCompletion);
			proposal.setFlags(refBinding.modifiers);
			int start = (this.assistNodeInJavadoc & CompletionOnJavadoc.REPLACE_TAG) != 0 ? this.javadocTagPosition : this.startPosition;
			proposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance+R_INLINE_TAG);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}
	private void createTypeVariable(TypeVariableBinding typeVariable, Scope scope, StringBuffer completion) {
		completion.append(typeVariable.sourceName);

		if (typeVariable.superclass != null && TypeBinding.equalsEquals(typeVariable.firstBound, typeVariable.superclass)) {
		    completion.append(' ');
		    completion.append(EXTENDS);
		    completion.append(' ');
		    createType(typeVariable.superclass, scope, completion);
		}
		if (typeVariable.superInterfaces != null && typeVariable.superInterfaces != Binding.NO_SUPERINTERFACES) {
		   if (TypeBinding.notEquals(typeVariable.firstBound, typeVariable.superclass)) {
			   completion.append(' ');
			   completion.append(EXTENDS);
			   completion.append(' ');
		   }
		   for (int i = 0, length = typeVariable.superInterfaces.length; i < length; i++) {
			   if (i > 0 || TypeBinding.equalsEquals(typeVariable.firstBound, typeVariable.superclass)) {
				   completion.append(' ');
				   completion.append(EXTENDS);
				   completion.append(' ');
			   }
			   createType(typeVariable.superInterfaces[i], scope, completion);
		   }
		}
	}
	private void createVargsType(TypeBinding type, Scope scope, StringBuffer completion) {
		if (type.isArrayType()) {
			createType(type.leafComponentType(), scope, completion);
			int dim = type.dimensions() - 1;
			for (int i = 0; i < dim; i++) {
				completion.append('[');
				completion.append(']');
			}
			completion.append(VARARGS);
		} else {
			createType(type, scope, completion);
		}
	}
	private void findAnnotationAttributes(char[] token, MemberValuePair[] attributesFound, ReferenceBinding annotation) {
		MethodBinding[] methods = annotation.availableMethods();
		nextAttribute: for (int i = 0; i < methods.length; i++) {
			MethodBinding method = methods[i];

			if(isFailedMatch(token, method.selector)) continue nextAttribute;

			int length = attributesFound == null ? 0 : attributesFound.length;
			for (int j = 0; j < length; j++) {
				if(CharOperation.equals(method.selector, attributesFound[j].name, false)) continue nextAttribute;
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal(method);
			relevance += computeRelevanceForCaseMatching(token, method.selector);
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.ANNOTATION_ATTRIBUTE_REF)) {
				CompletionProposal proposal = createProposal(CompletionProposal.ANNOTATION_ATTRIBUTE_REF, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method.returnType));
				proposal.setName(method.selector);
				// add "=" to completion since it will always be needed
				char[] completion= method.selector;
				if (JavaCore.INSERT.equals(this.javaProject.getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR, true))) {
					completion= CharOperation.concat(completion, new char[] {' '});
				}
				completion= CharOperation.concat(completion, new char[] {'='});
				if (JavaCore.INSERT.equals(this.javaProject.getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR, true))) {
					completion= CharOperation.concat(completion, new char[] {' '});
				}
				proposal.setCompletion(completion);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}
	void findAnonymousType(
			ReferenceBinding currentType,
			TypeBinding[] argTypes,
			Scope scope,
			InvocationSite invocationSite,
			Binding[] missingElements,
			int[] missingElementsStarts,
			int[] missingElementsEnds,
			boolean missingElementsHaveProblems) {

		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal(currentType);
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

		if (missingElements != null) {
			relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
		}

		findAnonymousType(
				currentType,
				argTypes,
				scope,
				invocationSite,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				true,
				false,
				relevance);
	}
	private void findAnonymousType(
		ReferenceBinding currentType,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		boolean exactMatch,
		boolean isQualified,
		int relevance) {

		if (currentType.isInterface()) {
			char[] completion = CharOperation.NO_CHAR;
			char[] typeCompletion = null;
			if (!exactMatch) {
				typeCompletion =
					isQualified ?
							CharOperation.concat(currentType.qualifiedPackageName(), currentType.qualifiedSourceName(), '.') :
								currentType.sourceName();
				if (this.source != null
							&& this.source.length > this.endPosition
							&& this.source[this.endPosition] == '(') {
					completion = CharOperation.NO_CHAR;
				} else {
					completion = new char[] { '(', ')' };
				}
			}

			this.noProposal = false;
			if (!exactMatch) {
				if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
					char[] packageName = currentType.isLocalType() ? null : currentType.qualifiedPackageName();
					char[] typeName = currentType.qualifiedSourceName();

					InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
					proposal.setDeclarationSignature(getSignature(currentType));
					proposal.setDeclarationKey(currentType.computeUniqueKey());
					proposal.setSignature(
							createMethodSignature(
									CharOperation.NO_CHAR_CHAR,
									CharOperation.NO_CHAR_CHAR,
									CharOperation.NO_CHAR,
									CharOperation.NO_CHAR));
					//proposal.setOriginalSignature(null);
					//proposal.setUniqueKey(null);
					proposal.setDeclarationPackageName(packageName);
					proposal.setDeclarationTypeName(typeName);
					//proposal.setParameterPackageNames(null);
					//proposal.setParameterTypeNames(null);
					//proposal.setPackageName(null);
					//proposal.setTypeName(null);
					proposal.setName(currentType.sourceName());

					InternalCompletionProposal typeProposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
					typeProposal.nameLookup = this.nameEnvironment.nameLookup;
					typeProposal.completionEngine = this;
					typeProposal.setDeclarationSignature(packageName);
					typeProposal.setSignature(getRequiredTypeSignature(currentType));
					typeProposal.setPackageName(packageName);
					typeProposal.setTypeName(typeName);
					typeProposal.setCompletion(typeCompletion);
					typeProposal.setFlags(currentType.modifiers);
					typeProposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					typeProposal.setTokenRange(this.startPosition - this.offset, this.endPosition - this.offset);
					typeProposal.setRelevance(relevance);
					proposal.setRequiredProposals( new CompletionProposal[]{typeProposal});

					proposal.setCompletion(completion);
					proposal.setFlags(Flags.AccPublic);
					proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}  else {
				if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, missingElements != null)) {
					InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, this.actualCompletionPosition);
					proposal.setDeclarationSignature(getSignature(currentType));
					proposal.setDeclarationKey(currentType.computeUniqueKey());
					proposal.setSignature(
							createMethodSignature(
									CharOperation.NO_CHAR_CHAR,
									CharOperation.NO_CHAR_CHAR,
									CharOperation.NO_CHAR,
									CharOperation.NO_CHAR));
					//proposal.setOriginalSignature(null);
					//proposal.setUniqueKey(null);
					proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
					proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
					//proposal.setParameterPackageNames(null);
					//proposal.setParameterTypeNames(null);
					//proposal.setPackageName(null);
					//proposal.setTypeName(null);
					if (missingElements != null) {
						CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
						for (int i = 0; i < missingElements.length; i++) {
							subProposals[i] =
								createRequiredTypeProposal(
										missingElements[i],
										missingElementsStarts[i],
										missingElementsEnds[i],
										relevance);
						}
						proposal.setRequiredProposals(subProposals);
					}
					proposal.setCompletion(completion);
					proposal.setFlags(Flags.AccPublic);
					proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenEnd - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		} else {
			findConstructors(
				currentType,
				argTypes,
				scope,
				invocationSite,
				true,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				exactMatch,
				isQualified,
				relevance);
		}
	}
	private void findClassField(
			char[] token,
			TypeBinding receiverType,
			Scope scope,
			Binding[] missingElements,
			int[] missingElementsStarts,
			int[] missingElementsEnds,
			boolean missingElementsHaveProblems) {

		if (token == null) return;

		if (token.length <= classField.length
			&& CharOperation.prefixEquals(token, classField, false /* ignore case */
		)) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(token, classField);
			relevance += computeRelevanceForExpectingType(scope.getJavaLangClass());
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); //no access restriction for class field
			relevance += R_NON_INHERITED;

			if (missingElements != null) {
				relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
			}

			this.noProposal = false;
			if(!isIgnored(CompletionProposal.FIELD_REF, missingElements != null)) {
				InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
				//proposal.setDeclarationSignature(null);
				char[] signature =
					createNonGenericTypeSignature(
						CharOperation.concatWith(JAVA_LANG, '.'),
						CLASS);
				if (this.compilerOptions.sourceLevel > ClassFileConstants.JDK1_4) {
					// add type argument
					char[] typeArgument = getTypeSignature(receiverType);
					int oldLength = signature.length;
					int argumentLength = typeArgument.length;
					int newLength = oldLength + argumentLength + 2;
					System.arraycopy(signature, 0, signature = new char[newLength], 0, oldLength - 1);
					signature[oldLength - 1] = '<';
					System.arraycopy(typeArgument, 0, signature, oldLength , argumentLength);
					signature[newLength - 2] = '>';
					signature[newLength - 1] = ';';
				}
				proposal.setSignature(signature);
				//proposal.setDeclarationPackageName(null);
				//proposal.setDeclarationTypeName(null);
				proposal.setPackageName(CharOperation.concatWith(JAVA_LANG, '.'));
				proposal.setTypeName(CLASS);
				proposal.setName(classField);
				if (missingElements != null) {
					CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
					for (int i = 0; i < missingElements.length; i++) {
						subProposals[i] =
							createRequiredTypeProposal(
									missingElements[i],
									missingElementsStarts[i],
									missingElementsEnds[i],
									relevance);
					}
					proposal.setRequiredProposals(subProposals);
				}
				proposal.setCompletion(classField);
				proposal.setFlags(Flags.AccStatic | Flags.AccPublic);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	void findConstructors(ReferenceBinding currentType, TypeBinding[] argTypes, Scope scope,
			InvocationSite invocationSite, boolean forAnonymousType, Binding[] missingElements,
			int[] missingElementsStarts, int[] missingElementsEnds, boolean missingElementsHaveProblems) {
		findConstructors(currentType, argTypes, scope, invocationSite, forAnonymousType, missingElements,
				missingElementsStarts, missingElementsEnds, missingElementsHaveProblems, new ObjectVector(), false);
	}

	void findConstructors(
		ReferenceBinding currentType,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite,
		boolean forAnonymousType,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		ObjectVector foundConstructors,
		boolean noCollection) {

		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

		if (missingElements != null) {
			relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
		}

		findConstructors(
				currentType,
				argTypes,
				scope,
				invocationSite,
				forAnonymousType,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				true,
				false,
				relevance,
				foundConstructors,
				noCollection);
	}


	private void findConstructorsFromMissingType(
			TypeReference typeRef,
			final TypeBinding[] argTypes,
			final Scope scope,
			final InvocationSite invocationSite) {
		MissingTypesGuesser missingTypesConverter = new MissingTypesGuesser(this);
		MissingTypesGuesser.GuessedTypeRequestor substitutionRequestor =
			new MissingTypesGuesser.GuessedTypeRequestor() {
				@Override
				public void accept(
						TypeBinding guessedType,
						Binding[] missingElements,
						int[] missingElementsStarts,
						int[] missingElementsEnds,
						boolean hasProblems) {
					if (guessedType instanceof ReferenceBinding) {
						ReferenceBinding ref = (ReferenceBinding) guessedType;
						if (!isIgnored(CompletionProposal.METHOD_REF, missingElements != null)
								&& ref.isClass()
								&& !ref.isAbstract()) {
								findConstructors(
									ref,
									argTypes,
									scope,
									invocationSite,
									false,
									missingElements,
									missingElementsStarts,
									missingElementsEnds,
									hasProblems);
						}

						checkCancel();

						if (!isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, missingElements != null)
								&& !ref.isFinal()
								&& !ref.isEnum()){
							findAnonymousType(
								ref,
								argTypes,
								scope,
								invocationSite,
								missingElements,
								missingElementsStarts,
								missingElementsEnds,
								hasProblems);
						}
					}
				}
			};
		missingTypesConverter.guess(typeRef, scope, substitutionRequestor);
	}

	private void findConstructors(ReferenceBinding currentType, TypeBinding[] argTypes, Scope scope,
			InvocationSite invocationSite, boolean forAnonymousType, Binding[] missingElements,
			int[] missingElementsStarts, int[] missingElementsEnds, boolean missingElementsHaveProblems,
			boolean exactMatch, boolean isQualified, int relevance) {
		findConstructors(currentType, argTypes, scope, invocationSite, forAnonymousType, missingElements,
				missingElementsStarts, missingElementsEnds, missingElementsHaveProblems, exactMatch, isQualified,
				relevance, new ObjectVector(), false);
	}

	private void findConstructors(
		ReferenceBinding currentType,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite,
		boolean forAnonymousType,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		boolean exactMatch,
		boolean isQualified,
		int relevance,
		ObjectVector constructorsFound,
		boolean noCollection) {

		// No visibility checks can be performed without the scope & invocationSite
		MethodBinding[] methods = null;
		if (currentType instanceof ParameterizedTypeBinding && invocationSite instanceof CompletionOnQualifiedAllocationExpression) {
			CompletionOnQualifiedAllocationExpression alloc = (CompletionOnQualifiedAllocationExpression) invocationSite;
			if ((alloc.bits & ASTNode.IsDiamond) != 0) {
				// inference failed. So don't substitute type arguments. Just return the unsubstituted methods
				// and let the user decide what to substitute.
				ParameterizedTypeBinding binding = (ParameterizedTypeBinding) currentType;
				ReferenceBinding originalGenericType = binding.genericType();
				if (originalGenericType != null)
					methods = originalGenericType.methods();
			} else {
				methods = currentType.availableMethods();
			}
		} else {
			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 (this.options.checkDeprecation &&
							constructor.isViewedAsDeprecated() &&
							!scope.isDefinedInSameUnit(constructor.declaringClass))
						continue next;

					if (this.options.checkVisibility
						&& !constructor.canBeSeenBy(invocationSite, scope)) {
						if(!forAnonymousType || !constructor.isProtected())
							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 (!argTypes[a].isCompatibleWith(constructor.parameters[a])
								// check if this type pair is parameterized types and their erasure types matches
									&& !argTypes[a].erasure().isCompatibleWith(constructor.parameters[a].erasure()))
								continue next;
						}

					constructorsFound.add(new Object[] { constructor, currentType });
					if (noCollection) {
						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 = CharOperation.NO_CHAR;

					if(forAnonymousType){
						char[] typeCompletion = null;
						if (!exactMatch) {
							typeCompletion =
								isQualified ?
										CharOperation.concat(currentType.qualifiedPackageName(), currentType.qualifiedSourceName(), '.') :
											currentType.sourceName();
							if (this.source != null
										&& this.source.length > this.endPosition
										&& this.source[this.endPosition] == '(') {
								completion = CharOperation.NO_CHAR;
							} else {
								completion = new char[] { '(', ')' };
							}
						}

						this.noProposal = false;
						if (!exactMatch) {
							if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
								char[] packageName = currentType.isLocalType() ? null : currentType.qualifiedPackageName();
								char[] typeName = currentType.qualifiedSourceName();

								InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
								proposal.setBinding(constructor);
								proposal.setDeclarationSignature(getSignature(currentType));
								proposal.setDeclarationKey(currentType.computeUniqueKey());
								proposal.setSignature(getSignature(constructor));
								MethodBinding original = constructor.original();
								if(original != constructor) {
									proposal.setOriginalSignature(getSignature(original));
								}
								proposal.setKey(constructor.computeUniqueKey());
								proposal.setDeclarationPackageName(packageName);
								proposal.setDeclarationTypeName(typeName);
								proposal.setParameterPackageNames(parameterPackageNames);
								proposal.setParameterTypeNames(parameterTypeNames);
								//proposal.setPackageName(null);
								//proposal.setTypeName(null);
								proposal.setName(currentType.sourceName());

								InternalCompletionProposal typeProposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
								typeProposal.nameLookup = this.nameEnvironment.nameLookup;
								typeProposal.completionEngine = this;
								typeProposal.setDeclarationSignature(packageName);
								typeProposal.setSignature(getRequiredTypeSignature(currentType));
								typeProposal.setPackageName(packageName);
								typeProposal.setTypeName(typeName);
								typeProposal.setCompletion(typeCompletion);
								typeProposal.setFlags(currentType.modifiers);
								typeProposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								typeProposal.setTokenRange(this.startPosition - this.offset, this.endPosition - this.offset);
								typeProposal.setRelevance(relevance);
								proposal.setRequiredProposals( new CompletionProposal[]{typeProposal});

								proposal.setCompletion(completion);
								proposal.setFlags(constructor.modifiers);
								proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance);
								if(parameterNames != null) proposal.setParameterNames(parameterNames);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						} else {
							if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, missingElements != null)) {
								InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_DECLARATION, this.actualCompletionPosition);
								proposal.setDeclarationSignature(getSignature(currentType));
								proposal.setDeclarationKey(currentType.computeUniqueKey());
								proposal.setSignature(getSignature(constructor));
								MethodBinding original = constructor.original();
								if(original != constructor) {
									proposal.setOriginalSignature(getSignature(original));
								}
								proposal.setKey(constructor.computeUniqueKey());
								proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
								proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
								proposal.setParameterPackageNames(parameterPackageNames);
								proposal.setParameterTypeNames(parameterTypeNames);
								//proposal.setPackageName(null);
								//proposal.setTypeName(null);
								if (missingElements != null) {
									CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
									for (int i = 0; i < missingElements.length; i++) {
										subProposals[i] =
											createRequiredTypeProposal(
													missingElements[i],
													missingElementsStarts[i],
													missingElementsEnds[i],
													relevance);
									}
									proposal.setRequiredProposals(subProposals);
								}
								proposal.setCompletion(completion);
								proposal.setFlags(constructor.modifiers);
								proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenEnd - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance);
								if(parameterNames != null) proposal.setParameterNames(parameterNames);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
					} else {
						char[] typeCompletion = null;
						// Special case for completion in javadoc
						if (this.assistNodeInJavadoc > 0) {
							Expression receiver = null;
							char[] selector = null;
							if (invocationSite instanceof CompletionOnJavadocAllocationExpression) {
								CompletionOnJavadocAllocationExpression alloc = (CompletionOnJavadocAllocationExpression) invocationSite;
								receiver = alloc.type;
							} else if (invocationSite instanceof CompletionOnJavadocFieldReference) {
								CompletionOnJavadocFieldReference fieldRef = (CompletionOnJavadocFieldReference) invocationSite;
								receiver = fieldRef.receiver;
							}
							if (receiver instanceof JavadocModuleReference) {
								JavadocModuleReference modRef = (JavadocModuleReference) receiver;
								receiver = modRef.typeReference;
							}
							if (receiver != null) {
								StringBuffer javadocCompletion = new StringBuffer();
								if (receiver.isThis()) {
									selector = (((JavadocImplicitTypeReference)receiver).token);
									if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0) {
										javadocCompletion.append('#');
									}
								} else if (receiver instanceof JavadocSingleTypeReference) {
									JavadocSingleTypeReference typeRef = (JavadocSingleTypeReference) receiver;
									selector = typeRef.token;
									if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0) {
										javadocCompletion.append(typeRef.token);
										javadocCompletion.append('#');
									}
								} else if (receiver instanceof JavadocQualifiedTypeReference) {
									JavadocQualifiedTypeReference typeRef = (JavadocQualifiedTypeReference) receiver;
									selector = typeRef.tokens[typeRef.tokens.length-1];
									if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0) {
										javadocCompletion.append(CharOperation.concatWith(typeRef.tokens, '.'));
										javadocCompletion.append('#');
									}
								}
								// Append parameters types
								javadocCompletion.append(selector);
								javadocCompletion.append('(');
								if (constructor.parameters != null) {
									boolean isVarargs = constructor.isVarargs();
									for (int p=0, ln=constructor.parameters.length; p<ln; p++) {
										if (p>0) javadocCompletion.append(", "); //$NON-NLS-1$
										TypeBinding argTypeBinding = constructor.parameters[p];
										if (isVarargs && p == ln - 1)  {
											createVargsType(argTypeBinding.erasure(), scope, javadocCompletion);
										} else {
											createType(argTypeBinding.erasure(), scope, javadocCompletion);
										}
									}
								}
								javadocCompletion.append(')');
								completion = javadocCompletion.toString().toCharArray();
							}
						} else {
							if (!exactMatch) {
								typeCompletion =
									isQualified ?
											CharOperation.concat(currentType.qualifiedPackageName(), currentType.qualifiedSourceName(), '.') :
												currentType.sourceName();

								if (this.source != null
											&& this.source.length > this.endPosition
											&& this.source[this.endPosition] == '(') {
									completion = CharOperation.NO_CHAR;
								} else {
									completion = new char[] { '(', ')' };
								}
							}
						}

						// Create standard proposal
						this.noProposal = false;
						if (!exactMatch) {
							if(!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
								char[] packageName = currentType.isLocalType() ? null : currentType.qualifiedPackageName();
								char[] typeName = currentType.qualifiedSourceName();
								int constructorRelevance = relevance + computeRelevanceForConstructor();
								InternalCompletionProposal proposal =  createProposal(CompletionProposal.CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
								proposal.setBinding(constructor);
								proposal.setDeclarationSignature(getSignature(currentType));
								proposal.setSignature(getSignature(constructor));
								MethodBinding original = constructor.original();
								if(original != constructor) {
									proposal.setOriginalSignature(getSignature(original));
								}
								proposal.setDeclarationPackageName(packageName);
								proposal.setDeclarationTypeName(typeName);
								proposal.setParameterPackageNames(parameterPackageNames);
								proposal.setParameterTypeNames(parameterTypeNames);
								//proposal.setPackageName(null);
								//proposal.setTypeName(null);
								proposal.setName(currentType.sourceName());

								InternalCompletionProposal typeProposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
								typeProposal.nameLookup = this.nameEnvironment.nameLookup;
								typeProposal.completionEngine = this;
								typeProposal.setDeclarationSignature(packageName);
								typeProposal.setSignature(getRequiredTypeSignature(currentType));
								typeProposal.setPackageName(packageName);
								typeProposal.setTypeName(typeName);
								typeProposal.setCompletion(typeCompletion);
								typeProposal.setFlags(currentType.modifiers);
								typeProposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								typeProposal.setTokenRange(this.startPosition - this.offset, this.endPosition - this.offset);
								typeProposal.setRelevance(constructorRelevance);
								proposal.setRequiredProposals( new CompletionProposal[]{typeProposal});

								proposal.setIsContructor(true);
								proposal.setCompletion(completion);
								proposal.setFlags(constructor.modifiers);
								proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(constructorRelevance);
								if(parameterNames != null) proposal.setParameterNames(parameterNames);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						} else {
							if(!isIgnored(CompletionProposal.METHOD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
								InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
								proposal.setBinding(constructor);
								proposal.setDeclarationSignature(getSignature(currentType));
								proposal.setSignature(getSignature(constructor));
								MethodBinding original = constructor.original();
								if(original != constructor) {
									proposal.setOriginalSignature(getSignature(original));
								}
								proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
								proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
								proposal.setParameterPackageNames(parameterPackageNames);
								proposal.setParameterTypeNames(parameterTypeNames);
								//proposal.setPackageName(null);
								//proposal.setTypeName(null);
								proposal.setName(currentType.sourceName());
								if (missingElements != null) {
									CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
									for (int i = 0; i < missingElements.length; i++) {
										subProposals[i] =
											createRequiredTypeProposal(
													missingElements[i],
													missingElementsStarts[i],
													missingElementsEnds[i],
													relevance);
									}
									proposal.setRequiredProposals(subProposals);
								}
								proposal.setIsContructor(true);
								proposal.setCompletion(completion);
								proposal.setFlags(constructor.modifiers);
								int start = (this.assistNodeInJavadoc > 0) ? this.startPosition : this.endPosition;
								proposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance);
								if(parameterNames != null) proposal.setParameterNames(parameterNames);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
							if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) {
								char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK);
								InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_METHOD_REF, this.actualCompletionPosition);
								proposal.setBinding(constructor);
								proposal.setDeclarationSignature(getSignature(currentType));
								proposal.setSignature(getSignature(constructor));
								MethodBinding original = constructor.original();
								if(original != constructor) {
									proposal.setOriginalSignature(getSignature(original));
								}
								proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
								proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
								proposal.setParameterPackageNames(parameterPackageNames);
								proposal.setParameterTypeNames(parameterTypeNames);
								//proposal.setPackageName(null);
								//proposal.setTypeName(null);
								proposal.setName(currentType.sourceName());
								proposal.setIsContructor(true);
								proposal.setCompletion(javadocCompletion);
								proposal.setFlags(constructor.modifiers);
								int start = (this.assistNodeInJavadoc & CompletionOnJavadoc.REPLACE_TAG) != 0 ? this.javadocTagPosition : this.startPosition;
								proposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance+R_INLINE_TAG);
								if(parameterNames != null) proposal.setParameterNames(parameterNames);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
					}
				}
			}
		}
	}

	private char[] getResolvedSignature(char[][] parameterTypes, char[] fullyQualifiedTypeName, int parameterCount, Scope scope) {
		char[][] cn = CharOperation.splitOn('.', fullyQualifiedTypeName);

		TypeReference ref;
		if (cn.length == 1) {
			ref = new SingleTypeReference(cn[0], 0);
		} else {
			ref = new QualifiedTypeReference(cn,new long[cn.length]);
		}

		TypeBinding guessedType = null;
		INameEnvironment oldNameEnvironment = this.lookupEnvironment.nameEnvironment;
		this.lookupEnvironment.nameEnvironment = getNoCacheNameEnvironment();
		try {
			switch (scope.kind) {
				case Scope.METHOD_SCOPE :
				case Scope.BLOCK_SCOPE :
					guessedType = ref.resolveType((BlockScope)scope);
					break;
				case Scope.CLASS_SCOPE :
					guessedType = ref.resolveType((ClassScope)scope);
					break;
			}


			if (guessedType != null && guessedType.isValidBinding()) {
				// the erasure must be used because guessedType can be a RawTypeBinding (https://bugs.eclipse.org/bugs/show_bug.cgi?id=276890)
				guessedType = guessedType.erasure();

				if (guessedType instanceof SourceTypeBinding) {
					SourceTypeBinding refBinding = (SourceTypeBinding) guessedType;

					if (refBinding.scope == null || refBinding.scope.referenceContext == null) return null;

					TypeDeclaration typeDeclaration = refBinding.scope.referenceContext;
					AbstractMethodDeclaration[] methods = typeDeclaration.methods;

					next : for (int i = 0; i < methods.length; i++) {
						AbstractMethodDeclaration method = methods[i];

						if (!method.isConstructor()) continue next;

						Argument[] arguments = method.arguments;
						int argumentsLength = arguments == null ? 0 : arguments.length;

						if (parameterCount != argumentsLength) continue next;

						for (int j = 0; j < argumentsLength; j++) {
							char[] argumentTypeName = getTypeName(arguments[j].type);

							if (!CharOperation.equals(argumentTypeName, parameterTypes[j])) {
								continue next;
							}
						}

						refBinding.resolveTypesFor(method.binding); // force resolution
						if (method.binding == null) continue next;
						return getSignature(method.binding);
					}
				}
			}
		} finally {
			this.lookupEnvironment.nameEnvironment = oldNameEnvironment;
		}

		return null;
	}

	private void findConstructorsOrAnonymousTypes(
			ReferenceBinding currentType,
			Scope scope,
			InvocationSite invocationSite,
			boolean isQualified,
			int relevance) {

		if (!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)
				&& currentType.isClass()
				&& !currentType.isAbstract()) {
				findConstructors(
					currentType,
					null,
					scope,
					invocationSite,
					false,
					null,
					null,
					null,
					false,
					false,
					isQualified,
					relevance);
		}

		// This code is disabled because there is too much proposals when constructors and anonymous are proposed
		if (!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)
				&& !currentType.isFinal()
				&& (currentType.isInterface() || (currentType.isClass() && currentType.isAbstract()))){
			findAnonymousType(
				currentType,
				null,
				scope,
				invocationSite,
				null,
				null,
				null,
				false,
				false,
				isQualified,
				relevance);
		}
	}
	private char[][] findEnclosingTypeNames(Scope scope){
		char[][] excludedNames = new char[10][];
		int excludedNameCount = 0;

		Scope currentScope = scope;
		while(currentScope != null) {
			switch (currentScope.kind) {
				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) currentScope;

					TypeDeclaration typeDeclaration = classScope.referenceContext;

					if(excludedNameCount == excludedNames.length) {
						System.arraycopy(excludedNames, 0, excludedNames = new char[excludedNameCount * 2][], 0, excludedNameCount);
					}
					excludedNames[excludedNameCount++] = typeDeclaration.name;

					TypeParameter[] classTypeParameters = typeDeclaration.typeParameters;
					if(classTypeParameters != null) {
						for (int i = 0; i < classTypeParameters.length; i++) {
							TypeParameter typeParameter = classTypeParameters[i];
							if(excludedNameCount == excludedNames.length) {
								System.arraycopy(excludedNames, 0, excludedNames = new char[excludedNameCount * 2][], 0, excludedNameCount);
							}
							excludedNames[excludedNameCount++] = typeParameter.name;
						}
					}
					break;
				case Scope.METHOD_SCOPE :
					MethodScope methodScope = (MethodScope) currentScope;
					if(methodScope.referenceContext instanceof AbstractMethodDeclaration) {
						TypeParameter[] methodTypeParameters = ((AbstractMethodDeclaration)methodScope.referenceContext).typeParameters();
						if(methodTypeParameters != null) {
							for (int i = 0; i < methodTypeParameters.length; i++) {
								TypeParameter typeParameter = methodTypeParameters[i];
								if(excludedNameCount == excludedNames.length) {
									System.arraycopy(excludedNames, 0, excludedNames = new char[excludedNameCount * 2][], 0, excludedNameCount);
								}
								excludedNames[excludedNameCount++] = typeParameter.name;
							}
						}
					}
					break;
			}

			currentScope = currentScope.parent;
		}

		if(excludedNameCount == 0) {
			return CharOperation.NO_CHAR_CHAR;
		}
		System.arraycopy(excludedNames, 0, excludedNames = new char[excludedNameCount][], 0, excludedNameCount);
		return excludedNames;
	}
	private void findEnumConstants(
			char[] enumConstantName,
			ReferenceBinding enumType,
			Scope invocationScope,
			ObjectVector fieldsFound,
			char[][] alreadyUsedConstants,
			int alreadyUsedConstantCount,
			boolean needQualification) {

		FieldBinding[] fields = enumType.fields();

		int enumConstantLength = enumConstantName.length;
		next : for (int f = fields.length; --f >= 0;) {
			FieldBinding field = fields[f];

			if (field.isSynthetic()) continue next;

			if ((field.modifiers & Flags.AccEnum) == 0) continue next;

			if (enumConstantLength > field.name.length) continue next;

			if (isFailedMatch(enumConstantName, field.name))	continue next;

			char[] fieldName = field.name;

			for (int i = 0; i < alreadyUsedConstantCount; i++) {
				if(CharOperation.equals(alreadyUsedConstants[i], fieldName)) continue next;
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal(field);
			relevance += computeRelevanceForCaseMatching(enumConstantName, field.name);
			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(field.type);
			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
				continue;
			} else if (!this.strictMatchForExtepectedType) {
				relevance += computeRelevanceForExpectingType;
			}

			relevance += computeRelevanceForEnumConstant(field.type);
			relevance += computeRelevanceForQualification(needQualification);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if (!needQualification) {
				char[] completion = fieldName;

				if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
					InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
					proposal.setBinding(field);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName());
					proposal.setName(field.name);
					proposal.setCompletion(completion);
					proposal.setFlags(field.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}

			} else {
				TypeBinding visibleType = invocationScope.getType(field.type.sourceName());
				boolean needImport = visibleType == null || !visibleType.isValidBinding();

				char[] completion = CharOperation.concat(field.type.sourceName(), field.name, '.');

				if (!needImport) {
					if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
						InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
						proposal.setDeclarationSignature(getSignature(field.declaringClass));
						proposal.setSignature(getSignature(field.type));
						proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
						proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
						proposal.setPackageName(field.type.qualifiedPackageName());
						proposal.setTypeName(field.type.qualifiedSourceName());
						proposal.setName(field.name);
						proposal.setCompletion(completion);
						proposal.setFlags(field.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				} else {
					if (!this.isIgnored(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_IMPORT)) {
						CompilationUnitDeclaration cu = this.unitScope.referenceContext;
						int importStart = cu.types[0].declarationSourceStart;
						int importEnd = importStart;

						ReferenceBinding fieldType = (ReferenceBinding)field.type;

						InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
						proposal.setBinding(field);
						proposal.setDeclarationSignature(getSignature(field.declaringClass));
						proposal.setSignature(getSignature(field.type));
						proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
						proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
						proposal.setPackageName(field.type.qualifiedPackageName());
						proposal.setTypeName(field.type.qualifiedSourceName());
						proposal.setName(field.name);
						proposal.setCompletion(completion);
						proposal.setFlags(field.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);

						char[] typeImportCompletion = createImportCharArray(CharOperation.concatWith(fieldType.compoundName, '.'), false, false);

						InternalCompletionProposal typeImportProposal = createProposal(CompletionProposal.TYPE_IMPORT, this.actualCompletionPosition);
						typeImportProposal.nameLookup = this.nameEnvironment.nameLookup;
						typeImportProposal.completionEngine = this;
						char[] packageName = fieldType.qualifiedPackageName();
						typeImportProposal.setDeclarationSignature(packageName);
						typeImportProposal.setSignature(getSignature(fieldType));
						typeImportProposal.setPackageName(packageName);
						typeImportProposal.setTypeName(fieldType.qualifiedSourceName());
						typeImportProposal.setCompletion(typeImportCompletion);
						typeImportProposal.setFlags(fieldType.modifiers);
						typeImportProposal.setAdditionalFlags(CompletionFlags.Default);
						typeImportProposal.setReplaceRange(importStart - this.offset, importEnd - this.offset);
						typeImportProposal.setTokenRange(importStart - this.offset, importEnd - this.offset);
						typeImportProposal.setRelevance(relevance);

						proposal.setRequiredProposals(new CompletionProposal[]{typeImportProposal});

						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}
		}
	}
	private void findEnumConstantsFromExpectedTypes(
			char[] token,
			Scope invocationScope,
			ObjectVector fieldsFound) {
		int length = this.expectedTypesPtr + 1;
		for (int i = 0; i < length; i++) {
			if (this.expectedTypes[i].isEnum()) {
				findEnumConstants(
						token,
						(ReferenceBinding)this.expectedTypes[i],
						invocationScope,
						fieldsFound,
						CharOperation.NO_CHAR_CHAR,
						0,
						true);
			}
		}

	}
	private void findEnumConstantsFromSwithStatement(char[] enumConstantName, SwitchStatement switchStatement) {
		TypeBinding expressionType = switchStatement.expression.resolvedType;
		if(expressionType != null && expressionType.isEnum()) {
			ReferenceBinding enumType = (ReferenceBinding) expressionType;

			CaseStatement[] cases = switchStatement.cases;

			char[][] alreadyUsedConstants = new char[switchStatement.caseCount][];
			int alreadyUsedConstantCount = 0;
			for (int i = 0; i < switchStatement.caseCount; i++) {
				Expression[] caseExpressions = cases[i].constantExpressions;
				Expression caseExpression = caseExpressions != null && caseExpressions.length > 0 ?
						caseExpressions[0] : null;
				if((caseExpression instanceof SingleNameReference)
						&& (caseExpression.resolvedType != null && caseExpression.resolvedType.isEnum())) {
					alreadyUsedConstants[alreadyUsedConstantCount++] = ((SingleNameReference)caseExpression).token;
				}
			}

			findEnumConstants(
					enumConstantName,
					enumType,
					null /* doesn't need invocation scope */,
					new ObjectVector(),
					alreadyUsedConstants,
					alreadyUsedConstantCount,
					false);
		}
	}
	private void findExceptionFromTryStatement(
			char[] typeName,
			ReferenceBinding exceptionType,
			ReferenceBinding receiverType,
			SourceTypeBinding invocationType,
			BlockScope scope,
			ObjectVector typesFound,
			boolean searchSuperClasses) {

		if (searchSuperClasses) {
			ReferenceBinding javaLangThrowable = scope.getJavaLangThrowable();
			if (TypeBinding.notEquals(exceptionType, javaLangThrowable)) {
				ReferenceBinding superClass = exceptionType.superclass();
				while(superClass != null && TypeBinding.notEquals(superClass, javaLangThrowable)) {
					findExceptionFromTryStatement(typeName, superClass, receiverType, invocationType, scope, typesFound, false);
					superClass = superClass.superclass();
				}
			}
		}

		if (typeName.length > exceptionType.sourceName.length)
			return;

		if (isFailedMatch(typeName, exceptionType.sourceName))
			return;

		if (this.options.checkDeprecation &&
				exceptionType.isViewedAsDeprecated() &&
				!scope.isDefinedInSameUnit(exceptionType))
			return;

		if (this.options.checkVisibility) {
			if (invocationType != null) {
				if (receiverType != null) {
					if (!exceptionType.canBeSeenBy(receiverType, invocationType)) return;
				} else {
					if (!exceptionType.canBeSeenBy(exceptionType, invocationType)) return;
				}
			} else if(!exceptionType.canBeSeenBy(this.unitScope.fPackage)) {
				return;
			}
		}

		if (isForbidden(exceptionType)) return;

		for (int j = typesFound.size; --j >= 0;) {
			ReferenceBinding otherType = (ReferenceBinding) typesFound.elementAt(j);

			if (TypeBinding.equalsEquals(exceptionType, otherType))
				return;

			if (CharOperation.equals(exceptionType.sourceName, otherType.sourceName, true)) {

				if (exceptionType.enclosingType().isSuperclassOf(otherType.enclosingType()))
					return;

				if (otherType.enclosingType().isInterface())
					if (exceptionType.enclosingType()
						.implementsInterface(otherType.enclosingType(), true))
						return;

				if (exceptionType.enclosingType().isInterface())
					if (otherType.enclosingType()
						.implementsInterface(exceptionType.enclosingType(), true))
						return;
			}
		}

		typesFound.add(exceptionType);

		char[] completionName = exceptionType.sourceName();

		boolean isQualified = false;

		if(!this.insideQualifiedReference) {
			isQualified = true;

			char[] memberPackageName = exceptionType.qualifiedPackageName();
			char[] memberTypeName = exceptionType.sourceName();
			char[] memberEnclosingTypeNames = null;

			ReferenceBinding enclosingType = exceptionType.enclosingType();
			if (enclosingType != null) {
				memberEnclosingTypeNames = exceptionType.enclosingType().qualifiedSourceName();
			}

			Scope currentScope = scope;
			done : while (currentScope != null) { // done when a COMPILATION_UNIT_SCOPE is found

				switch (currentScope.kind) {

					case Scope.METHOD_SCOPE :
					case Scope.BLOCK_SCOPE :
						BlockScope blockScope = (BlockScope) currentScope;

						for (int j = 0, length = blockScope.subscopeCount; j < length; j++) {

							if (blockScope.subscopes[j] instanceof ClassScope) {
								SourceTypeBinding localType =
									((ClassScope) blockScope.subscopes[j]).referenceContext.binding;

								if (TypeBinding.equalsEquals(localType, exceptionType)) {
									isQualified = false;
									break done;
								}
							}
						}
						break;

					case Scope.CLASS_SCOPE :
						SourceTypeBinding type = ((ClassScope)currentScope).referenceContext.binding;
						ReferenceBinding[] memberTypes = type.memberTypes();
						if (memberTypes != null) {
							for (int j = 0; j < memberTypes.length; j++) {
								if (TypeBinding.equalsEquals(memberTypes[j], exceptionType)) {
									isQualified = false;
									break done;
								}
							}
						}


						break;

					case Scope.COMPILATION_UNIT_SCOPE :
						SourceTypeBinding[] types = ((CompilationUnitScope)currentScope).topLevelTypes;
						if (types != null) {
							for (int j = 0; j < types.length; j++) {
								if (TypeBinding.equalsEquals(types[j], exceptionType)) {
									isQualified = false;
									break done;
								}
							}
						}
						break done;
				}
				currentScope = currentScope.parent;
			}

			if (isQualified && mustQualifyType(memberPackageName, memberTypeName, memberEnclosingTypeNames, exceptionType.modifiers)) {
				if (memberPackageName == null || memberPackageName.length == 0)
					if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
						return; // ignore types from the default package from outside it
			} else {
				isQualified = false;
			}

			if (isQualified) {
				completionName =
					CharOperation.concat(
							memberPackageName,
							CharOperation.concat(
									memberEnclosingTypeNames,
									memberTypeName,
									'.'),
							'.');
			}
		}

		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal(exceptionType);
		relevance += computeRelevanceForCaseMatching(typeName, exceptionType.sourceName);
		relevance += computeRelevanceForExpectingType(exceptionType);
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
		if(!this.insideQualifiedReference) {
			relevance += computeRelevanceForQualification(isQualified);
		}
		relevance += computeRelevanceForClass();
		relevance += computeRelevanceForException();

		this.noProposal = false;
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			createTypeProposal(
					exceptionType,
					exceptionType.qualifiedSourceName(),
					IAccessRule.K_ACCESSIBLE,
					completionName,
					relevance,
					null,
					null,
					null,
					false);
		}
	}
	private void findExceptionFromTryStatement(
			char[] typeName,
			ReferenceBinding receiverType,
			SourceTypeBinding invocationType,
			BlockScope scope,
			ObjectVector typesFound) {

		for (int i = 0; i <= this.expectedTypesPtr; i++) {
			ReferenceBinding exceptionType = (ReferenceBinding)this.expectedTypes[i];

			findExceptionFromTryStatement(typeName, exceptionType, receiverType, invocationType, scope, typesFound, true);
		}
	}
	private void findExplicitConstructors(
		char[] name,
		ReferenceBinding currentType,
		MethodScope scope,
		InvocationSite invocationSite) {

		ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration)scope.referenceContext;
		MethodBinding enclosingConstructor = constructorDeclaration.binding;

		// No visibility checks can be performed without the scope & invocationSite
		MethodBinding[] methods = currentType.availableMethods();
		if(methods != null) {
			next : for (int f = methods.length; --f >= 0;) {
				MethodBinding constructor = methods[f];
				if (constructor != enclosingConstructor && constructor.isConstructor()) {

					if (constructor.isSynthetic()) continue next;

					if (this.options.checkDeprecation &&
							constructor.isViewedAsDeprecated() &&
							!scope.isDefinedInSameUnit(constructor.declaringClass))
						continue next;

					if (this.options.checkVisibility
						&& !constructor.canBeSeenBy(invocationSite, scope))	continue next;

					TypeBinding[] parameters = constructor.parameters;
					int paramLength = parameters.length;

					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 = CharOperation.NO_CHAR;
					if (this.source != null
						&& this.source.length > this.endPosition
						&& this.source[this.endPosition] == '(')
						completion = name;
					else
						completion = CharOperation.concat(name, new char[] { '(', ')' });

					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForResolution();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(this.completionToken, name);
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
						proposal.setBinding(constructor);
						proposal.setDeclarationSignature(getSignature(currentType));
						proposal.setSignature(getSignature(constructor));
						MethodBinding original = constructor.original();
						if(original != constructor) {
							proposal.setOriginalSignature(getSignature(original));
						}
						proposal.setDeclarationPackageName(currentType.qualifiedPackageName());
						proposal.setDeclarationTypeName(currentType.qualifiedSourceName());
						proposal.setParameterPackageNames(parameterPackageNames);
						proposal.setParameterTypeNames(parameterTypeNames);
						//proposal.setPackageName(null);
						//proposal.setTypeName(null);
						proposal.setName(name);
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(constructor.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						if(parameterNames != null) proposal.setParameterNames(parameterNames);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}
		}
	}
	// 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,
		boolean canBePrefixed,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		char[] castedReceiver,
		int receiverStart,
		int receiverEnd) {

		ObjectVector newFieldsFound = new ObjectVector();
		// if the proposal is being asked inside a field's initialization, we'll record its id
		int fieldBeingCompletedId = -1;
		boolean isFieldBeingCompletedStatic = false;
		for (int f = fields.length; --f >=0;) {
			FieldBinding field = fields[f];
			FieldDeclaration fieldDeclaration = field.sourceField();
			// We maybe asking for a proposal inside this field's initialization. So record its id
			ASTNode astNode = this.parser.assistNode;
			if (fieldDeclaration != null && fieldDeclaration.initialization != null && astNode != null) {
				if (CharOperation.equals(this.fileName, field.declaringClass.getFileName()) && fieldDeclaration.initialization.sourceEnd > 0) {
					if (fieldDeclaration.initialization.sourceStart <= astNode.sourceStart &&
						astNode.sourceEnd <= fieldDeclaration.initialization.sourceEnd) {
						// completion is inside a field initializer
						fieldBeingCompletedId = field.id;
						isFieldBeingCompletedStatic = field.isStatic();
						break;
					}
				} else { // The sourceEnd may not yet be set
					CompletionNodeDetector detector = new CompletionNodeDetector(astNode, fieldDeclaration.initialization);
					if (detector.containsCompletionNode()) {  // completion is inside a field initializer
						fieldBeingCompletedId = field.id;
						isFieldBeingCompletedStatic = field.isStatic();
						break;
					}
				}
			}
		}

		char[] boostMatches = findParameterNameAtLocationFromAssistParent(new ObjectVector(), scope);

		// 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];

			// Content assist invoked inside some field's initialization.
			// bug 310427 and 325481
			if (fieldBeingCompletedId >= 0 && field.id >= fieldBeingCompletedId) {
				// Don't propose field which is being declared currently
				// Don't propose fields declared after the current field declaration statement
				// Though, if field is static or completion happens in Javadoc, then it can be still be proposed
				if (this.assistNodeInJavadoc == 0) {
					if (!field.isStatic()) {
						continue next;
					} else if (isFieldBeingCompletedStatic) {
						// static fields can't be proposed before they are actually declared if the
						// field currently being declared is also static
						continue next;
					}
				}
			}

			if (field.isSynthetic())	continue next;

			if (onlyStaticFields && !field.isStatic()) continue next;

			if (fieldLength > field.name.length) continue next;

			if (isFailedMatch(fieldName, field.name))	continue next;

			if (this.options.checkDeprecation &&
					field.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(field.declaringClass))
				continue next;

			if (this.options.checkVisibility
				&& !field.canBeSeenBy(receiverType, invocationSite, scope))	continue next;

			// don't propose non constant fields or strings (1.6 or below) in case expression
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=195346
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=343342
			if (this.assistNodeIsInsideCase) {
				if (field.isFinal()) {
					if (this.assistNodeIsString){
						if (field.type == null || field.type.id != TypeIds.T_JavaLangString)
							continue next;
					} else if (!(field.type instanceof BaseTypeBinding))
						continue next;
				} else {
					continue next; // non-constants not allowed in case.
				}
			}

			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 && TypeBinding.equalsEquals(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 (TypeBinding.equalsEquals(field.declaringClass, scope.getJavaLangObject()))
							continue next;
						if (field.declaringClass.implementsInterface(otherField.declaringClass, true))
							continue next;
					}
					if (field.declaringClass.isInterface())
						if (otherField.declaringClass.implementsInterface(field.declaringClass, true))
							continue next;
					if(canBePrefixed) {
						prefixRequired = true;
					} else {
						continue next;
					}
				}
			}

			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() && TypeBinding.notEquals(declarationType, invocationScope.enclosingSourceType())) {
						continue next;
					}
					if(canBePrefixed) {
						prefixRequired = true;
					} else {
						continue next;
					}
					break;
				}
			}

			newFieldsFound.add(new Object[]{field, receiverType});

			char[] completion = field.name;

			if(prefixRequired || this.options.forceImplicitQualification){
				char[] prefix = computePrefix(scope.enclosingSourceType(), invocationScope.enclosingSourceType(), field.isStatic());
				completion = CharOperation.concat(prefix,completion,'.');
			}


			if (castedReceiver != null) {
				completion = CharOperation.concat(castedReceiver, completion);
			}

			// Special case for javadoc completion
			if (this.assistNodeInJavadoc > 0) {
				if (invocationSite instanceof CompletionOnJavadocFieldReference) {
					CompletionOnJavadocFieldReference fieldRef = (CompletionOnJavadocFieldReference) invocationSite;
					if (fieldRef.receiver.isThis()) {
						if (fieldRef.completeInText()) {
							completion = CharOperation.concat(new char[] { '#' }, field.name);
						}
					} else if (fieldRef.completeInText()) {
						if (fieldRef.receiver instanceof JavadocSingleTypeReference) {
							JavadocSingleTypeReference typeRef = (JavadocSingleTypeReference) fieldRef.receiver;
							completion = CharOperation.concat(typeRef.token, field.name, '#');
						} else if (fieldRef.receiver instanceof JavadocQualifiedTypeReference) {
							JavadocQualifiedTypeReference typeRef = (JavadocQualifiedTypeReference) fieldRef.receiver;
							completion = CharOperation.concat(CharOperation.concatWith(typeRef.tokens, '.'), field.name, '#');
						}
					}
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal(field);
			relevance += computeRelevanceForCaseMatching(fieldName, field.name);
			if(boostMatches.length > 0) {
				relevance += computeRelevanceForCaseMatching(boostMatches, field.name);
			}
			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(field.type);
			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
				continue;
			} else if(!this.strictMatchForExtepectedType) {
				relevance += computeRelevanceForExpectingType;
			}

			relevance += computeRelevanceForEnumConstant(field.type);
			relevance += computeRelevanceForStatic(onlyStaticFields, field.isStatic());
			relevance += computeRelevanceForFinal(this.assistNodeIsInsideCase, field.isFinal());
			relevance += computeRelevanceForQualification(prefixRequired);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			if (onlyStaticFields && this.insideQualifiedReference) {
				relevance += computeRelevanceForInheritance(receiverType, field.declaringClass);
			}
			if (missingElements != null) {
				relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
			}

			this.noProposal = false;
			if (castedReceiver == null) {
				// Standard proposal
				if (!this.isIgnored(CompletionProposal.FIELD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
					proposal.setBinding(field);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName());
					proposal.setName(field.name);
					if (missingElements != null) {
						CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
						for (int i = 0; i < missingElements.length; i++) {
							subProposals[i] =
								createRequiredTypeProposal(
										missingElements[i],
										missingElementsStarts[i],
										missingElementsEnds[i],
										relevance);
						}
						proposal.setRequiredProposals(subProposals);
					}
					proposal.setCompletion(completion);
					proposal.setFlags(field.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}

				// Javadoc completions
				if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_FIELD_REF)) {
					char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK);
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_FIELD_REF, this.actualCompletionPosition);
					proposal.setBinding(field);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName());
					proposal.setName(field.name);
					proposal.setCompletion(javadocCompletion);
					proposal.setFlags(field.modifiers);
					int start = (this.assistNodeInJavadoc & CompletionOnJavadoc.REPLACE_TAG) != 0 ? this.javadocTagPosition : this.startPosition;
					proposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance+R_INLINE_TAG);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
					// Javadoc value completion for static fields
					if (field.isStatic() && !this.requestor.isIgnored(CompletionProposal.JAVADOC_VALUE_REF)) {
						javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_VALUE);
						InternalCompletionProposal valueProposal = createProposal(CompletionProposal.JAVADOC_VALUE_REF, this.actualCompletionPosition);
						valueProposal.setDeclarationSignature(getSignature(field.declaringClass));
						valueProposal.setSignature(getSignature(field.type));
						valueProposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
						valueProposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
						valueProposal.setPackageName(field.type.qualifiedPackageName());
						valueProposal.setTypeName(field.type.qualifiedSourceName());
						valueProposal.setName(field.name);
						valueProposal.setCompletion(javadocCompletion);
						valueProposal.setFlags(field.modifiers);
						valueProposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
						valueProposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						valueProposal.setRelevance(relevance+R_VALUE_TAG);
						this.requestor.accept(valueProposal);
						if(DEBUG) {
							this.printDebug(valueProposal);
						}
					}
				}
			} else {
				if(!this.isIgnored(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, missingElements != null)) {
					InternalCompletionProposal proposal = createProposal(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition);
					proposal.setBinding(field);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setReceiverSignature(getSignature(receiverType));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName());
					proposal.setName(field.name);
					if (missingElements != null) {
						CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
						for (int i = 0; i < missingElements.length; i++) {
							subProposals[i] =
								createRequiredTypeProposal(
										missingElements[i],
										missingElementsStarts[i],
										missingElementsEnds[i],
										relevance);
						}
						proposal.setRequiredProposals(subProposals);
					}
					proposal.setCompletion(completion);
					proposal.setFlags(field.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setReceiverRange(receiverStart - this.offset, receiverEnd - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}

		fieldsFound.addAll(newFieldsFound);
	}
	private void findFields(
		char[] fieldName,
		ReferenceBinding receiverType,
		Scope scope,
		ObjectVector fieldsFound,
		ObjectVector localsFound,
		boolean onlyStaticFields,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean canBePrefixed,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		char[] castedReceiver,
		int receiverStart,
		int receiverEnd) {

		boolean notInJavadoc = this.assistNodeInJavadoc == 0;
		if (fieldName == null && notInJavadoc)
			return;

		ReferenceBinding currentType = receiverType;
		ReferenceBinding[] interfacesToVisit = null;
		int nextPosition = 0;
		do {
			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (notInJavadoc && itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
				if (interfacesToVisit == null) {
					interfacesToVisit = itsInterfaces;
					nextPosition = interfacesToVisit.length;
				} else {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}

			FieldBinding[] fields = currentType.availableFields();
			if(fields != null && fields.length > 0) {
				findFields(
					fieldName,
					fields,
					scope,
					fieldsFound,
					localsFound,
					onlyStaticFields,
					receiverType,
					invocationSite,
					invocationScope,
					implicitCall,
					canBePrefixed,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems,
					castedReceiver,
					receiverStart,
					receiverEnd);
			}
			currentType = currentType.superclass();
		} while (notInJavadoc && currentType != null);

		if (notInJavadoc && interfacesToVisit != null) {
			for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding anInterface = interfacesToVisit[i];
				FieldBinding[] fields = anInterface.availableFields();
				if(fields !=  null) {
					findFields(
						fieldName,
						fields,
						scope,
						fieldsFound,
						localsFound,
						onlyStaticFields,
						receiverType,
						invocationSite,
						invocationScope,
						implicitCall,
						canBePrefixed,
						missingElements,
						missingElementsStarts,
						missingElementsEnds,
						missingElementsHaveProblems,
						castedReceiver,
						receiverStart,
						receiverEnd);
				}

				ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
				if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}

	protected void findFieldsAndMethods(
		char[] token,
		TypeBinding receiverType,
		Scope scope,
		ObjectVector fieldsFound,
		ObjectVector methodsFound,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		char[] castedReceiver,
		int receiverStart,
		int receiverEnd) {

		if (token == null)
			return;

		if (receiverType.isBaseType())
			return; // nothing else is possible with base types

		boolean proposeField =
			castedReceiver == null ?
					!this.isIgnored(CompletionProposal.FIELD_REF, missingElements != null) :
					!this.isIgnored(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, missingElements != null) ;
		boolean proposeMethod =
			castedReceiver == null ?
					!this.isIgnored(CompletionProposal.METHOD_REF, missingElements != null) :
					!this.isIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, missingElements != null);

		if (receiverType.isArrayType()) {
			if (proposeField
				&& token.length <= lengthField.length
				&& CharOperation.prefixEquals(token, lengthField, false /* ignore case */
			)) {

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(token,lengthField);
				relevance += computeRelevanceForExpectingType(TypeBinding.INT);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for length field
				if (missingElements != null) {
					relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
				}
				this.noProposal = false;
				if (castedReceiver == null) {
					if(!isIgnored(CompletionProposal.FIELD_REF, missingElements != null)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
						proposal.setDeclarationSignature(getSignature(receiverType));
						proposal.setSignature(INT_SIGNATURE);
						proposal.setTypeName(INT);
						proposal.setName(lengthField);
						if (missingElements != null) {
							CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
							for (int i = 0; i < missingElements.length; i++) {
								subProposals[i] =
									createRequiredTypeProposal(
											missingElements[i],
											missingElementsStarts[i],
											missingElementsEnds[i],
											relevance);
							}
							proposal.setRequiredProposals(subProposals);
						}
						proposal.setCompletion(lengthField);
						proposal.setFlags(Flags.AccPublic);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				} else {
					char[] completion = CharOperation.concat(castedReceiver, lengthField);

					if(!this.isIgnored(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, missingElements != null)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition);
						proposal.setDeclarationSignature(getSignature(receiverType));
						proposal.setSignature(INT_SIGNATURE);
						proposal.setReceiverSignature(getSignature(receiverType));
						proposal.setTypeName(INT);
						proposal.setName(lengthField);
						if (missingElements != null) {
							CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
							for (int i = 0; i < missingElements.length; i++) {
								subProposals[i] =
									createRequiredTypeProposal(
											missingElements[i],
											missingElementsStarts[i],
											missingElementsEnds[i],
											relevance);
							}
							proposal.setRequiredProposals(subProposals);
						}
						proposal.setCompletion(completion);
						proposal.setFlags(Flags.AccPublic);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setReceiverRange(receiverStart - this.offset, receiverEnd - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}
			if (proposeMethod
				&& token.length <= cloneMethod.length
				&& CharOperation.prefixEquals(token, cloneMethod, false /* ignore case */)
			) {
				ReferenceBinding objectRef = scope.getJavaLangObject();

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(token, cloneMethod);
				relevance += computeRelevanceForExpectingType(objectRef);
				relevance += computeRelevanceForStatic(false, false);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for clone() method
				if (missingElements != null) {
					relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
				}
				char[] completion;
				if (this.source != null
					&& this.source.length > this.endPosition
					&& this.source[this.endPosition] == '(') {
					completion = cloneMethod;
					} else {
					completion = CharOperation.concat(cloneMethod, new char[] { '(', ')' });
				}

				if (castedReceiver != null) {
					completion = CharOperation.concat(castedReceiver, completion);
				}

				this.noProposal = false;
				if (castedReceiver == null) {
					if (!this.isIgnored(CompletionProposal.METHOD_REF, missingElements != null)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
						proposal.setDeclarationSignature(getSignature(receiverType));
						proposal.setSignature(
								this.compilerOptions.sourceLevel > ClassFileConstants.JDK1_4 && receiverType.isArrayType() ?
										createMethodSignature(
												CharOperation.NO_CHAR_CHAR,
												CharOperation.NO_CHAR_CHAR,
												getSignature(receiverType)) :
										createMethodSignature(
												CharOperation.NO_CHAR_CHAR,
												CharOperation.NO_CHAR_CHAR,
												CharOperation.concatWith(JAVA_LANG, '.'),
												OBJECT));
						//proposal.setOriginalSignature(null);
						//proposal.setDeclarationPackageName(null);
						//proposal.setDeclarationTypeName(null);
						//proposal.setParameterPackageNames(null);
						//proposal.setParameterTypeNames(null);
						proposal.setPackageName(CharOperation.concatWith(JAVA_LANG, '.'));
						proposal.setTypeName(OBJECT);
						proposal.setName(cloneMethod);
						if (missingElements != null) {
							CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
							for (int i = 0; i < missingElements.length; i++) {
								subProposals[i] =
									createRequiredTypeProposal(
											missingElements[i],
											missingElementsStarts[i],
											missingElementsEnds[i],
											relevance);
							}
							proposal.setRequiredProposals(subProposals);
						}
						proposal.setCompletion(completion);
						proposal.setFlags(Flags.AccPublic);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
					methodsFound.add(new Object[]{objectRef.getMethods(cloneMethod)[0], objectRef});
				} else {
					if(!this.isIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, missingElements != null)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition);
						proposal.setDeclarationSignature(getSignature(receiverType));
						proposal.setSignature(
								this.compilerOptions.sourceLevel > ClassFileConstants.JDK1_4 && receiverType.isArrayType() ?
										createMethodSignature(
												CharOperation.NO_CHAR_CHAR,
												CharOperation.NO_CHAR_CHAR,
												getSignature(receiverType)) :
										createMethodSignature(
												CharOperation.NO_CHAR_CHAR,
												CharOperation.NO_CHAR_CHAR,
												CharOperation.concatWith(JAVA_LANG, '.'),
												OBJECT));
						proposal.setReceiverSignature(getSignature(receiverType));
						proposal.setPackageName(CharOperation.concatWith(JAVA_LANG, '.'));
						proposal.setTypeName(OBJECT);
						proposal.setName(cloneMethod);
						if (missingElements != null) {
							CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
							for (int i = 0; i < missingElements.length; i++) {
								subProposals[i] =
									createRequiredTypeProposal(
											missingElements[i],
											missingElementsStarts[i],
											missingElementsEnds[i],
											relevance);
							}
							proposal.setRequiredProposals(subProposals);
						}
						proposal.setCompletion(completion);
						proposal.setFlags(Flags.AccPublic);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setReceiverRange(receiverStart - this.offset, receiverEnd - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
			}

			receiverType = scope.getJavaLangObject();
		}

		checkCancel();

		if(proposeField) {
			findFields(
				token,
				(ReferenceBinding) receiverType,
				scope,
				fieldsFound,
				new ObjectVector(),
				false,
				invocationSite,
				invocationScope,
				implicitCall,
				false,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				castedReceiver,
				receiverStart,
				receiverEnd);
		}

		if(proposeMethod) {
			findMethods(
				token,
				null,
				null,
				(ReferenceBinding) receiverType,
				scope,
				methodsFound,
				false,
				false,
				invocationSite,
				invocationScope,
				implicitCall,
				superCall,
				false,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				castedReceiver,
				receiverStart,
				receiverEnd);
		}
	}

	protected void findFieldsAndMethodsFromAnotherReceiver(
			char[] token,
			TypeReference receiverType,
			Scope scope,
			ObjectVector fieldsFound,
			ObjectVector methodsFound,
			InvocationSite invocationSite,
			Scope invocationScope,
			boolean implicitCall,
			boolean superCall,
			Binding[] missingElements,
			int[] missingElementsStarts,
			int[] missingElementsEnds,
			boolean missingElementsHaveProblems,
			char[][] receiverName,
			int receiverStart,
			int receiverEnd) {

		if (receiverType.resolvedType == null) return;

		TypeBinding receiverTypeBinding = receiverType.resolvedType;
		char[] castedReceiver = null;

		char[] castedTypeChars = CharOperation.concatWith(receiverType.getTypeName(), '.');
		if(this.source != null) {
			int memberRefStart = this.startPosition;

			char[] receiverChars = CharOperation.subarray(this.source, receiverStart, receiverEnd);
			char[] dotChars = CharOperation.subarray(this.source, receiverEnd, memberRefStart);

			castedReceiver =
				CharOperation.concat(
					CharOperation.concat(
						'(',
						CharOperation.concat(
							CharOperation.concat('(', castedTypeChars, ')'),
							receiverChars),
						')'),
					dotChars);
		} else {
			castedReceiver =
				CharOperation.concat(
					CharOperation.concat(
						'(',
						CharOperation.concat(
							CharOperation.concat('(', castedTypeChars, ')'),
							CharOperation.concatWith(receiverName, '.')),
						')'),
					DOT);
		}

		if (castedReceiver == null) return;

		int oldStartPosition = this.startPosition;
		this.startPosition = receiverStart;

		findFieldsAndMethods(
				token,
				receiverTypeBinding,
				scope,
				fieldsFound,
				methodsFound,
				invocationSite,
				invocationScope,
				implicitCall,
				superCall,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				castedReceiver,
				receiverStart,
				receiverEnd);

		this.startPosition = oldStartPosition;
	}
	private void findFieldsAndMethodsFromCastedReceiver(
			ASTNode enclosingNode,
			Binding qualifiedBinding,
			Scope scope,
			ObjectVector fieldsFound,
			ObjectVector methodsFound,
			InvocationSite invocationSite,
			Scope invocationScope,
			Expression receiver) {

		if (enclosingNode == null) return;

		ASTNode currentEnclosing = enclosingNode;
		while (currentEnclosing != null) {
			InstanceOfExpression instanceOfExpression = findGuardingInstanceOf(currentEnclosing);
			if (instanceOfExpression == null) return;

			TypeReference instanceOfType = instanceOfExpression.type;

			if (instanceOfType.resolvedType == null) return;

			boolean findFromAnotherReceiver = false;

			char[][] receiverName = null;
			int receiverStart = -1;
			int receiverEnd = -1;

			if (receiver instanceof QualifiedNameReference) {
				QualifiedNameReference qualifiedNameReference = (QualifiedNameReference) receiver;

				receiverName = qualifiedNameReference.tokens;

				if (receiverName.length != 1) return;

				receiverStart = (int) (qualifiedNameReference.sourcePositions[0] >>> 32);
				receiverEnd = (int) qualifiedNameReference.sourcePositions[qualifiedNameReference.tokens.length - 1] + 1;

				// if (local instanceof X) local.|
				// if (field instanceof X) field.|
				if (instanceOfExpression.expression instanceof SingleNameReference &&
						((SingleNameReference)instanceOfExpression.expression).binding == qualifiedBinding &&
						(qualifiedBinding instanceof LocalVariableBinding || qualifiedBinding instanceof FieldBinding)) {
					findFromAnotherReceiver = true;
				}

				// if (this.field instanceof X) field.|
				if (instanceOfExpression.expression instanceof FieldReference) {
					FieldReference fieldReference = (FieldReference)instanceOfExpression.expression;

					if (fieldReference.receiver instanceof ThisReference &&
							qualifiedBinding instanceof FieldBinding &&
							fieldReference.binding == qualifiedBinding) {
								findFromAnotherReceiver = true;
					}
				}
			} else if (receiver instanceof FieldReference) {
				FieldReference fieldReference1 = (FieldReference) receiver;

				receiverStart = fieldReference1.sourceStart;
				receiverEnd = fieldReference1.sourceEnd + 1;

				if (fieldReference1.receiver instanceof ThisReference) {

					receiverName = new char[][] {THIS, fieldReference1.token};

					// if (field instanceof X) this.field.|
					if (instanceOfExpression.expression instanceof SingleNameReference &&
							((SingleNameReference)instanceOfExpression.expression).binding == fieldReference1.binding) {
						findFromAnotherReceiver = true;
					}

					// if (this.field instanceof X) this.field.|
					if (instanceOfExpression.expression instanceof FieldReference) {
						FieldReference fieldReference2 = (FieldReference)instanceOfExpression.expression;

						if (fieldReference2.receiver instanceof ThisReference &&
								fieldReference2.binding == fieldReference1.binding) {
									findFromAnotherReceiver = true;
						}
					}
				}
			}

			if (findFromAnotherReceiver) {
				TypeBinding receiverTypeBinding = instanceOfType.resolvedType;
				char[] castedReceiver = null;

				char[] castedTypeChars = CharOperation.concatWith(instanceOfType.getTypeName(), '.');
				if(this.source != null) {
					int memberRefStart = this.startPosition;

					char[] receiverChars = CharOperation.subarray(this.source, receiverStart, receiverEnd);
					char[] dotChars = CharOperation.subarray(this.source, receiverEnd, memberRefStart);

					castedReceiver =
						CharOperation.concat(
							CharOperation.concat(
								'(',
								CharOperation.concat(
									CharOperation.concat('(', castedTypeChars, ')'),
									receiverChars),
								')'),
							dotChars);
				} else {
					castedReceiver =
						CharOperation.concat(
							CharOperation.concat(
								'(',
								CharOperation.concat(
									CharOperation.concat('(', castedTypeChars, ')'),
									CharOperation.concatWith(receiverName, '.')),
								')'),
							DOT);
				}

				if (castedReceiver == null) return;

				int oldStartPosition = this.startPosition;
				this.startPosition = receiverStart;

				findFieldsAndMethods(
						this.completionToken,
						receiverTypeBinding,
						scope,
						fieldsFound,
						methodsFound,
						invocationSite,
						invocationScope,
						false,
						false,
						null,
						null,
						null,
						false,
						castedReceiver,
						receiverStart,
						receiverEnd);

				this.startPosition = oldStartPosition;
			}
			// traverse the enclosing node to find the instanceof expression corresponding
			// to the completion node (if any)
			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=304006
			currentEnclosing = findGuardedInner(currentEnclosing);
		}
	}

	private InstanceOfExpression findGuardingInstanceOf(ASTNode node) {
		// CompletionNodeDetector selects the enclosing node such that it will contain the assist node only in the positive branch.
		// Thus we don't have to check here in which branch we are asking for completion.
		ASTNode left = null;
		if (node instanceof IfStatement) {
			left = ((IfStatement) node).condition;
		} else if (node instanceof AND_AND_Expression) {
			left = ((AND_AND_Expression) node).left;
		} else {
			return null;
		}
		if (left instanceof InstanceOfExpression) {
			return (InstanceOfExpression) left;
		}
		return null;
	}

	private ASTNode findGuardedInner(ASTNode currentEnclosing) {
		if (currentEnclosing instanceof IfStatement) {
			Statement thenStatement = ((IfStatement) currentEnclosing).thenStatement;
			if (thenStatement instanceof Block) {
				Block block = (Block) thenStatement;
				if (block.statements != null && block.statements.length > 0)
					return block.statements[0];
			}
			return thenStatement;
		} else if (currentEnclosing instanceof AND_AND_Expression) {
			return ((AND_AND_Expression) currentEnclosing).right;
		}
		return null;
	}

	private void findFieldsAndMethodsFromFavorites(
			char[] token,
			Scope scope,
			InvocationSite invocationSite,
			Scope invocationScope,
			ObjectVector localsFound,
			ObjectVector fieldsFound,
			ObjectVector methodsFound) {

		ObjectVector methodsFoundFromFavorites = new ObjectVector();

		ImportBinding[] favoriteBindings = getFavoriteReferenceBindings(invocationScope);

		if (favoriteBindings != null && favoriteBindings.length > 0) {
			for (int i = 0; i < favoriteBindings.length; i++) {
				ImportBinding favoriteBinding = favoriteBindings[i];
				switch (favoriteBinding.resolvedImport.kind()) {
					case Binding.FIELD:
						FieldBinding fieldBinding = (FieldBinding) favoriteBinding.resolvedImport;
						findFieldsFromFavorites(
								token,
								new FieldBinding[]{fieldBinding},
								scope,
								fieldsFound,
								localsFound,
								fieldBinding.declaringClass,
								invocationSite,
								invocationScope);
						break;
					case Binding.METHOD:
						MethodBinding methodBinding = (MethodBinding) favoriteBinding.resolvedImport;
						MethodBinding[] methods = methodBinding.declaringClass.availableMethods();
						long range;
						if ((range = ReferenceBinding.binarySearch(methodBinding.selector, methods)) >= 0) {
							int start = (int) range, end = (int) (range >> 32);
							int length = end - start + 1;
							System.arraycopy(methods, start, methods = new MethodBinding[length], 0, length);
						} else {
							methods = Binding.NO_METHODS;
						}
						findLocalMethodsFromFavorites(
								token,
								methods,
								scope,
								methodsFound,
								methodsFoundFromFavorites,
								methodBinding.declaringClass,
								invocationSite,
								invocationScope);
						break;
					case Binding.TYPE:
						ReferenceBinding referenceBinding = (ReferenceBinding) favoriteBinding.resolvedImport;
						if(favoriteBinding.onDemand) {
							findFieldsFromFavorites(
									token,
									referenceBinding.availableFields(),
									scope,
									fieldsFound,
									localsFound,
									referenceBinding,
									invocationSite,
									invocationScope);

							findLocalMethodsFromFavorites(
									token,
									referenceBinding.availableMethods(),
									scope,
									methodsFound,
									methodsFoundFromFavorites,
									referenceBinding,
									invocationSite,
									invocationScope);
						}
						break;
				}
			}
		}

		methodsFound.addAll(methodsFoundFromFavorites);
	}

	private boolean findFieldsAndMethodsFromMissingFieldType(
		char[] token,
		Scope scope,
		InvocationSite invocationSite,
		boolean insideTypeAnnotation) {

		boolean foundSomeFields = false;

		Scope currentScope = scope;

		done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found

			switch (currentScope.kind) {

				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) currentScope;
					if(!insideTypeAnnotation) {

						FieldDeclaration[] fields = classScope.referenceContext.fields;

						int fieldsCount = fields == null ? 0 : fields.length;
						for (int i = 0; i < fieldsCount; i++) {
							FieldDeclaration fieldDeclaration = fields[i];
							if (CharOperation.equals(fieldDeclaration.name, token)) {
								FieldBinding fieldBinding = fieldDeclaration.binding;
								if (fieldBinding == null || fieldBinding.type == null  || (fieldBinding.type.tagBits & TagBits.HasMissingType) != 0) {
									foundSomeFields = true;
									findFieldsAndMethodsFromMissingType(
											fieldDeclaration.type,
											currentScope,
											invocationSite,
											scope);
								}
								break done;
							}
						}
					}
					insideTypeAnnotation = false;
					break;
				case Scope.COMPILATION_UNIT_SCOPE :
					break done;
			}
			currentScope = currentScope.parent;
		}
		return foundSomeFields;
	}

	private void findFieldsAndMethodsFromMissingReturnType(
		char[] token,
		TypeBinding[] arguments,
		Scope scope,
		InvocationSite invocationSite,
		boolean insideTypeAnnotation) {

		Scope currentScope = scope;

		done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found

			switch (currentScope.kind) {

				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) currentScope;
					if(!insideTypeAnnotation) {

						AbstractMethodDeclaration[] methods = classScope.referenceContext.methods;

						int methodsCount = methods == null ? 0 : methods.length;
						for (int i = 0; i < methodsCount; i++) {
							AbstractMethodDeclaration methodDeclaration = methods[i];
							if (methodDeclaration instanceof MethodDeclaration &&
									CharOperation.equals(methodDeclaration.selector, token)) {
								MethodDeclaration method = (MethodDeclaration) methodDeclaration;
								MethodBinding methodBinding = method.binding;
								if (methodBinding == null || methodBinding.returnType == null  || (methodBinding.returnType.tagBits & TagBits.HasMissingType) != 0) {
									Argument[] parameters = method.arguments;
									int parametersLength = parameters == null ? 0 : parameters.length;
									int argumentsLength = arguments == null ? 0 : arguments.length;

									if (parametersLength == 0) {
										if (argumentsLength == 0) {
											findFieldsAndMethodsFromMissingType(
													method.returnType,
													currentScope,
													invocationSite,
													scope);
											break done;
										}
									} else {
										TypeBinding[] parametersBindings;
										if (methodBinding == null) { // since no binding, extra types from type references
											parametersBindings = new TypeBinding[parametersLength];
											for (int j = 0; j < parametersLength; j++) {
												TypeBinding parameterType = parameters[j].type.resolvedType;
												if (!parameterType.isValidBinding() && parameterType.closestMatch() != null) {
													parameterType = parameterType.closestMatch();
												}
												parametersBindings[j] = parameterType;
											}
										} else {
											parametersBindings = methodBinding.parameters;
										}
										if(areParametersCompatibleWith(parametersBindings, arguments, parameters[parametersLength - 1].isVarArgs())) {
											findFieldsAndMethodsFromMissingType(
													method.returnType,
													currentScope,
													invocationSite,
													scope);
											break done;
										}
									}
								}

							}
						}
					}
					insideTypeAnnotation = false;
					break;
				case Scope.COMPILATION_UNIT_SCOPE :
					break done;
			}
			currentScope = currentScope.parent;
		}
	}

	private void findFieldsAndMethodsFromMissingType(
			TypeReference typeRef,
			final Scope scope,
			final InvocationSite invocationSite,
			final Scope invocationScope) {
		MissingTypesGuesser missingTypesConverter = new MissingTypesGuesser(this);
		MissingTypesGuesser.GuessedTypeRequestor substitutionRequestor =
			new MissingTypesGuesser.GuessedTypeRequestor() {
				@Override
				public void accept(
						TypeBinding guessedType,
						Binding[] missingElements,
						int[] missingElementsStarts,
						int[] missingElementsEnds,
						boolean hasProblems) {
					findFieldsAndMethods(
						CompletionEngine.this.completionToken,
						guessedType,
						scope,
						new ObjectVector(),
						new ObjectVector(),
						invocationSite,
						invocationScope,
						false,
						false,
						missingElements,
						missingElementsStarts,
						missingElementsEnds,
						hasProblems,
						null,
						-1,
						-1);

				}
			};
		missingTypesConverter.guess(typeRef, scope, substitutionRequestor);
	}

	private void findFieldsAndMethodsFromStaticImports(
			char[] token,
			Scope scope,
			InvocationSite invocationSite,
			Scope invocationScope,
			boolean exactMatch,
			boolean insideAnnotationAttribute,
			ObjectVector localsFound,
			ObjectVector fieldsFound,
			ObjectVector methodsFound,
			boolean proposeField,
			boolean proposeMethod) {
		// search in static import
		ImportBinding[] importBindings = scope.compilationUnitScope().imports;
		for (int i = 0; i < importBindings.length; i++) {
			ImportBinding importBinding = importBindings[i];
			if(importBinding.isValidBinding() && importBinding.isStatic()) {
				Binding binding = importBinding.resolvedImport;
				if(binding != null && binding.isValidBinding()) {
					if(importBinding.onDemand) {
						if((binding.kind() & Binding.TYPE) != 0) {
							if(proposeField) {
								findFields(
									token,
									(ReferenceBinding)binding,
									scope,
									fieldsFound,
									localsFound,
									true,
									invocationSite,
									invocationScope,
									true,
									false,
									null,
									null,
									null,
									false,
									null,
									-1,
									-1);
							}
							if(proposeMethod && !insideAnnotationAttribute) {
								findMethods(
									token,
									null,
									null,
									(ReferenceBinding)binding,
									scope,
									methodsFound,
									true,
									exactMatch,
									invocationSite,
									invocationScope,
									true,
									false,
									false,
									null,
									null,
									null,
									false,
									null,
									-1,
									-1);
							}
						}
					} else {
						if ((binding.kind() & Binding.FIELD) != 0) {
							if(proposeField) {
									findFields(
											token,
											new FieldBinding[]{(FieldBinding)binding},
											scope,
											fieldsFound,
											localsFound,
											true,
											((FieldBinding)binding).declaringClass,
											invocationSite,
											invocationScope,
											true,
											false,
											null,
											null,
											null,
											false,
											null,
											-1,
											-1);
							}
						} else if ((binding.kind() & Binding.METHOD) != 0) {
							if(proposeMethod && !insideAnnotationAttribute) {
								MethodBinding methodBinding = (MethodBinding)binding;
								if ((exactMatch && CharOperation.equals(token, methodBinding.selector)) ||
										!exactMatch && CharOperation.prefixEquals(token, methodBinding.selector, false) ||
										(this.options.camelCaseMatch && CharOperation.camelCaseMatch(token, methodBinding.selector))) {
									findLocalMethodsFromStaticImports(
											token,
											methodBinding.declaringClass.getMethods(methodBinding.selector),
											scope,
											exactMatch,
											methodsFound,
											methodBinding.declaringClass,
											invocationSite);
								}
							}
						}
					}
				}
			}
		}
	}

	private void findFieldsFromFavorites(
			char[] fieldName,
			FieldBinding[] fields,
			Scope scope,
			ObjectVector fieldsFound,
			ObjectVector localsFound,
			ReferenceBinding receiverType,
			InvocationSite invocationSite,
			Scope invocationScope) {

		char[] typeName = CharOperation.concatWith(receiverType.compoundName, '.');

		int fieldLength = fieldName.length;
		next : for (int f = fields.length; --f >= 0;) {
			FieldBinding field = fields[f];

			if (field.isSynthetic())	continue next;

			// only static fields must be proposed
			if (!field.isStatic()) continue next;

			if (fieldLength > field.name.length) continue next;

			if (isFailedMatch(fieldName, field.name))	continue next;

			if (this.options.checkDeprecation &&
					field.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(field.declaringClass))
				continue next;

			if (this.options.checkVisibility
				&& !field.canBeSeenBy(receiverType, invocationSite, scope))	continue next;

			for (int i = fieldsFound.size; --i >= 0;) {
				Object[] other = (Object[])fieldsFound.elementAt(i);
				FieldBinding otherField = (FieldBinding) other[0];

				if (field == otherField) continue next;
			}

			fieldsFound.add(new Object[]{field, receiverType});

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal(field);
			relevance += computeRelevanceForCaseMatching(fieldName, field.name);
			relevance += computeRelevanceForExpectingType(field.type);
			relevance += computeRelevanceForEnumConstant(field.type);
			relevance += computeRelevanceForStatic(true, true);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			CompilationUnitDeclaration cu = this.unitScope.referenceContext;
			int importStart = cu.types[0].declarationSourceStart;
			int importEnd = importStart;

			this.noProposal = false;

			if (this.compilerOptions.complianceLevel < ClassFileConstants.JDK1_5 ||
					!this.options.suggestStaticImport) {
				if (!this.isIgnored(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_IMPORT)) {
					char[] completion = CharOperation.concat(receiverType.sourceName, field.name, '.');

					InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
					proposal.setBinding(field);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName());
					proposal.setName(field.name);
					proposal.setCompletion(completion);
					proposal.setFlags(field.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);

					char[] typeImportCompletion = createImportCharArray(typeName, false, false);

					InternalCompletionProposal typeImportProposal = createProposal(CompletionProposal.TYPE_IMPORT, this.actualCompletionPosition);
					typeImportProposal.nameLookup = this.nameEnvironment.nameLookup;
					typeImportProposal.completionEngine = this;
					char[] packageName = receiverType.qualifiedPackageName();
					typeImportProposal.setDeclarationSignature(packageName);
					typeImportProposal.setSignature(getSignature(receiverType));
					typeImportProposal.setPackageName(packageName);
					typeImportProposal.setTypeName(receiverType.qualifiedSourceName());
					typeImportProposal.setCompletion(typeImportCompletion);
					typeImportProposal.setFlags(receiverType.modifiers);
					typeImportProposal.setAdditionalFlags(CompletionFlags.Default);
					typeImportProposal.setReplaceRange(importStart - this.offset, importEnd - this.offset);
					typeImportProposal.setTokenRange(importStart - this.offset, importEnd - this.offset);
					typeImportProposal.setRelevance(relevance);

					proposal.setRequiredProposals(new CompletionProposal[]{typeImportProposal});

					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			} else {
				if (!this.isIgnored(CompletionProposal.FIELD_REF, CompletionProposal.FIELD_IMPORT)) {
					char[] completion = field.name;

					InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
					proposal.setBinding(field);
					proposal.setDeclarationSignature(getSignature(field.declaringClass));
					proposal.setSignature(getSignature(field.type));
					proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					proposal.setPackageName(field.type.qualifiedPackageName());
					proposal.setTypeName(field.type.qualifiedSourceName());
					proposal.setName(field.name);
					proposal.setCompletion(completion);
					proposal.setFlags(field.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);

					char[] fieldImportCompletion = createImportCharArray(CharOperation.concat(typeName, field.name, '.'), true, false);

					InternalCompletionProposal fieldImportProposal = createProposal(CompletionProposal.FIELD_IMPORT, this.actualCompletionPosition);
					fieldImportProposal.setDeclarationSignature(getSignature(field.declaringClass));
					fieldImportProposal.setSignature(getSignature(field.type));
					fieldImportProposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
					fieldImportProposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
					fieldImportProposal.setPackageName(field.type.qualifiedPackageName());
					fieldImportProposal.setTypeName(field.type.qualifiedSourceName());
					fieldImportProposal.setName(field.name);
					fieldImportProposal.setCompletion(fieldImportCompletion);
					fieldImportProposal.setFlags(field.modifiers);
					fieldImportProposal.setAdditionalFlags(CompletionFlags.StaticImport);
					fieldImportProposal.setReplaceRange(importStart - this.offset, importEnd - this.offset);
					fieldImportProposal.setTokenRange(importStart - this.offset, importEnd - this.offset);
					fieldImportProposal.setRelevance(relevance);

					proposal.setRequiredProposals(new CompletionProposal[]{fieldImportProposal});

					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}
	private void findImplicitMessageSends(
		char[] token,
		TypeBinding[] argTypes,
		Scope scope,
		InvocationSite invocationSite,
		Scope invocationScope,
		ObjectVector methodsFound) {

		if (token == null)
			return;

		boolean staticsOnly = false;
		// need to know if we're in a static context (or inside a constructor)

		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,
						null,
						argTypes,
						enclosingType,
						classScope,
						methodsFound,
						staticsOnly,
						true,
						invocationSite,
						invocationScope,
						true,
						false,
						true,
						null,
						null,
						null,
						false,
						null,
						-1,
						-1);
					staticsOnly |= enclosingType.isStatic();
					break;

				case Scope.COMPILATION_UNIT_SCOPE :
					break done;
			}
			scope = scope.parent;
		}
	}
	private void findImports(CompletionOnImportReference importReference, boolean findMembers) {
		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[]{'.'});

		this.resolvingImports = true;
		this.resolvingStaticImports = importReference.isStatic();

		this.completionToken =  lastToken;
		this.qualifiedCompletionToken = importName;

		// want to replace the existing .*;
		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			int oldStart = this.startPosition;
			int oldEnd = this.endPosition;
			setSourceRange(
				importReference.sourceStart,
				importReference.declarationSourceEnd);
			try {
				this.nameEnvironment.findPackages(importName, this, this.javaProject.getAllPackageFragmentRoots(), true);
			} catch (JavaModelException e) {
				// silent
			}
			setSourceRange(
				oldStart,
				oldEnd - 1,
				false);
		}
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			this.foundTypesCount = 0;
			this.nameEnvironment.findTypes(
					importName,
					findMembers,
					getTypesMatchRule(),
					IJavaSearchConstants.TYPE,
					false,
					this,
					this.monitor);
			acceptTypes(null);
		}
	}

	private void findImportsOfMemberTypes(char[] typeName,	ReferenceBinding ref, boolean onlyStatic) {
		ReferenceBinding[] memberTypes = ref.memberTypes();

		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 (onlyStatic && !memberType.isStatic())
				continue next;

			if (typeLength > memberType.sourceName.length)
				continue next;

			if (isFailedMatch(typeName, memberType.sourceName))
				continue next;

			if (this.options.checkDeprecation && memberType.isViewedAsDeprecated()) continue next;

			if (this.options.checkVisibility
				&& !memberType.canBeSeenBy(this.unitScope.fPackage))
				continue next;

			char[] completionName = CharOperation.concat(memberType.sourceName, SEMICOLON);

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(typeName, memberType.sourceName);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			if (memberType.isClass()) {
				relevance += computeRelevanceForClass();
			} else if(memberType.isEnum()) {
				relevance += computeRelevanceForEnum();
			} else if (memberType.isInterface()) {
				relevance += computeRelevanceForInterface();
			}
			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				createTypeProposal(
						memberType,
						memberType.qualifiedSourceName(),
						IAccessRule.K_ACCESSIBLE,
						completionName,
						relevance,
						null,
						null,
						null,
						false);
			}
		}
	}

	private void findImportsOfStaticFields(char[] fieldName, ReferenceBinding ref) {
		FieldBinding[] fields = ref.availableFields();

		int fieldLength = fieldName.length;
		next : for (int m = fields.length; --m >= 0;) {
			FieldBinding field = fields[m];

			if (fieldLength > field.name.length)
				continue next;

			if (field.isSynthetic())
				continue next;

			if (!field.isStatic())
				continue next;

			if (isFailedMatch(fieldName, field.name))
				continue next;

			if (this.options.checkDeprecation && field.isViewedAsDeprecated()) continue next;

			if (this.options.checkVisibility
				&& !field.canBeSeenBy(this.unitScope.fPackage))
				continue next;

			char[] completionName = CharOperation.concat(field.name, SEMICOLON);

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(fieldName, field.name);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.FIELD_REF, this.actualCompletionPosition);
				proposal.setBinding(field);
				proposal.setDeclarationSignature(getSignature(field.declaringClass));
				proposal.setSignature(getSignature(field.type));
				proposal.setDeclarationPackageName(field.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(field.declaringClass.qualifiedSourceName());
				proposal.setPackageName(field.type.qualifiedPackageName());
				proposal.setTypeName(field.type.qualifiedSourceName());
				proposal.setName(field.name);
				proposal.setCompletion(completionName);
				proposal.setFlags(field.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	private void findImportsOfStaticMethods(char[] methodName, ReferenceBinding ref) {
		MethodBinding[] methods = ref.availableMethods();

		int methodLength = methodName.length;
		next : for (int m = methods.length; --m >= 0;) {
			MethodBinding method = methods[m];

			if (method.isSynthetic()) continue next;

			if (method.isDefaultAbstract())	continue next;

			if (method.isConstructor()) continue next;

			if (!method.isStatic()) continue next;

			if (this.options.checkDeprecation && method.isViewedAsDeprecated()) continue next;

			if (this.options.checkVisibility
				&& !method.canBeSeenBy(this.unitScope.fPackage)) continue next;

			if (methodLength > method.selector.length)
				continue next;

			if (isFailedMatch(methodName, method.selector))
				continue next;

			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.original().parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterTypeNames[i] = type.qualifiedSourceName();
			}
			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

			char[] completionName = CharOperation.concat(method.selector, SEMICOLON);

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_NAME_REFERENCE)) {
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_NAME_REFERENCE, this.actualCompletionPosition);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method));
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setName(method.selector);
				proposal.setCompletion(completionName);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	private void findInterfacesMethodDeclarations(
		char[] selector,
		ReferenceBinding receiverType,
		ReferenceBinding[] itsInterfaces,
		Scope scope,
		ObjectVector methodsFound,
		Binding[] missingElements,
		int[] missingElementssStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems) {

		if (selector == null)
			return;

		if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
			ReferenceBinding[] interfacesToVisit = itsInterfaces;
			int nextPosition = interfacesToVisit.length;

			for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding currentType = interfacesToVisit[i];
				MethodBinding[] methods = currentType.availableMethods();
				if(methods != null) {
					findLocalMethodDeclarations(
						selector,
						methods,
						scope,
						methodsFound,
						false,
						receiverType);
				}

				itsInterfaces = currentType.superInterfaces();
				if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}

	private void findInterfacesMethods(
		char[] selector,
		TypeBinding[] typeArgTypes,
		TypeBinding[] argTypes,
		ReferenceBinding receiverType,
		ReferenceBinding[] itsInterfaces,
		Scope scope,
		ObjectVector methodsFound,
		boolean onlyStaticMethods,
		boolean exactMatch,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		boolean canBePrefixed,
		Binding[] missingElements,
		int[] missingElementssStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		char[] castedReceiver,
		int receiverStart,
		int receiverEnd) {

		if (selector == null)
			return;

		if (itsInterfaces != Binding.NO_SUPERINTERFACES) {
			ReferenceBinding[] interfacesToVisit = itsInterfaces;
			int nextPosition = interfacesToVisit.length;

			for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding currentType = interfacesToVisit[i];
				MethodBinding[] methods = currentType.availableMethods();
				if(methods != null) {
					findLocalMethods(
						selector,
						typeArgTypes,
						argTypes,
						methods,
						scope,
						methodsFound,
						onlyStaticMethods,
						exactMatch,
						receiverType,
						invocationSite,
						invocationScope,
						implicitCall,
						superCall,
						canBePrefixed,
						missingElements,
						missingElementssStarts,
						missingElementsEnds,
						missingElementsHaveProblems,
						castedReceiver,
						receiverStart,
						receiverEnd);
				}

				itsInterfaces = currentType.superInterfaces();
				if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}
	/*
	 * Find javadoc block tags for a given completion javadoc tag node
	 */
	private void findJavadocBlockTags(CompletionOnJavadocTag javadocTag) {
		char[][] possibleTags = javadocTag.getPossibleBlockTags();
		if (possibleTags == null) return;
		int length = possibleTags.length;
		for (int i=0; i<length; i++) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors

			this.noProposal = false;
			if (!this.requestor.isIgnored(CompletionProposal.JAVADOC_BLOCK_TAG)) {
				char[] possibleTag = possibleTags[i];
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_BLOCK_TAG, this.actualCompletionPosition);
				proposal.setName(possibleTag);
				int tagLength = possibleTag.length;
				char[] completion = new char[1+tagLength];
				completion[0] = '@';
				System.arraycopy(possibleTag, 0, completion, 1, tagLength);
				proposal.setCompletion(completion);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if (DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	/*
	 * Find javadoc inline tags for a given completion javadoc tag node
	 */
	private void findJavadocInlineTags(CompletionOnJavadocTag javadocTag) {
		char[][] possibleTags = javadocTag.getPossibleInlineTags();
		if (possibleTags == null) return;
		int length = possibleTags.length;
		for (int i=0; i<length; i++) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors

			this.noProposal = false;
			if (!this.requestor.isIgnored(CompletionProposal.JAVADOC_INLINE_TAG)) {
				char[] possibleTag = possibleTags[i];
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_INLINE_TAG, this.actualCompletionPosition);
				proposal.setName(possibleTag);
				int tagLength = possibleTag.length;
//				boolean inlineTagStarted = javadocTag.completeInlineTagStarted();
				char[] completion = new char[2+tagLength+1];
				completion[0] = '{';
				completion[1] = '@';
				System.arraycopy(possibleTag, 0, completion, 2, tagLength);
				// do not add space at end of inline tag (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=121026)
				//completion[tagLength+2] = ' ';
				completion[tagLength+2] = '}';
				proposal.setCompletion(completion);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if (DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	private void findJavadocInSnippetTags(CompletionOnJavadocTag javadocTag) {
		char[][] possibleTags = javadocTag.getPossibleInSnippetTags();
		if (possibleTags == null) return;
		int length = possibleTags.length;
		for (int i=0; i<length; i++) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors

			this.noProposal = false;
			if (!this.requestor.isIgnored(CompletionProposal.JAVADOC_INLINE_TAG)) {
				char[] possibleTag = possibleTags[i];
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_BLOCK_TAG, this.actualCompletionPosition);
				proposal.setName(possibleTag);
				int tagLength = possibleTag.length;
				char[] completion = new char[1+tagLength];
				completion[0] = '@';
				System.arraycopy(possibleTag, 0, completion, 1, tagLength);
				proposal.setCompletion(completion);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				this.requestor.accept(proposal);
				if (DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
	}

	/*
	 * Find javadoc parameter names.
	 */
	private void findJavadocParamNames(char[] token, char[][] missingParams, boolean isTypeParam) {

		if (missingParams == null) return;

		// Get relevance
		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for param name
		if (!isTypeParam) relevance += R_INTERESTING;

		// Propose missing param
		int length = missingParams.length;
		relevance += length;
		for (int i=0; i<length; i++) {
			char[] argName = missingParams[i];
			if (token == null || CharOperation.prefixEquals(token, argName)) {

				this.noProposal = false;
				if (!this.requestor.isIgnored(CompletionProposal.JAVADOC_PARAM_REF)) {
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_PARAM_REF, this.actualCompletionPosition);
					proposal.setName(argName);
					char[] completion = isTypeParam ? CharOperation.concat('<', argName, '>') : argName;
					proposal.setCompletion(completion);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(--relevance);
					this.requestor.accept(proposal);
					if (DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}

	// what about onDemand types? Ignore them since it does not happen!
	// import p1.p2.A.*;
	private void findKeywords(char[] keyword, char[][] choices, boolean staticFieldsAndMethodOnly, boolean ignorePackageKeyword) {
		if(choices == null || choices.length == 0) return;
		int length = keyword.length;
		for (int i = 0; i < choices.length; i++)
			if (length <= choices[i].length && (CharOperation.prefixEquals(keyword, choices[i], false /* ignore case */)
					|| (this.options.substringMatch && CharOperation.substringMatch(keyword, choices[i])))) {
				if (ignorePackageKeyword && CharOperation.equals(choices[i], Keywords.PACKAGE))
					continue;
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(keyword, choices[i]);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywords
				relevance += computeRelevanceForFinalInIOE();
				if (staticFieldsAndMethodOnly && this.insideQualifiedReference) relevance += R_NON_INHERITED;

				if(CharOperation.equals(choices[i], Keywords.TRUE) || CharOperation.equals(choices[i], Keywords.FALSE)) {
					relevance += computeRelevanceForExpectingType(TypeBinding.BOOLEAN);
					relevance += computeRelevanceForQualification(false);
				}
				if (CharOperation.equals(choices[i], Keywords.NEW)) {
					relevance += computeRelevanceForConstructor();
				}
				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.KEYWORD, this.actualCompletionPosition);
					proposal.setName(choices[i]);
					proposal.setCompletion(choices[i]);
					proposal.setReplaceRange((this.startPosition < 0) ? 0 : this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange((this.tokenStart < 0) ? 0 : this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
	}

	private int computeRelevanceForFinalInIOE() {
		// add expected type else final keyword will be buried inside multiple Fin* classes
		return this.parser.assistNodeParent instanceof InstanceOfExpression ? R_FINAL + R_EXPECTED_TYPE : 0;
	}

	private void findKeywordsForMember(char[] token, int modifiers, ASTNode astNode) {
		char[][] keywords = new char[Keywords.COUNT][];
		int count = 0;

		// visibility
		if((modifiers & ClassFileConstants.AccPrivate) == 0
			&& (modifiers & ClassFileConstants.AccProtected) == 0
			&& (modifiers & ClassFileConstants.AccPublic) == 0) {
			keywords[count++] = Keywords.PROTECTED;
			keywords[count++] = Keywords.PUBLIC;
			if((modifiers & ClassFileConstants.AccAbstract) == 0) {
				keywords[count++] = Keywords.PRIVATE;
			}
		}

		if (astNode instanceof CompletionOnFieldType &&
	        this.compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8) {
	        FieldBinding astNodeBinding = ((CompletionOnFieldType) astNode).binding;
	        ReferenceBinding declaringClass = astNodeBinding != null ? astNodeBinding.declaringClass : null;
	        if (declaringClass != null && declaringClass.isInterface() && !declaringClass.isAnnotationType())
	            keywords[count++] = Keywords.DEFAULT;
	    }
		if((modifiers & ClassFileConstants.AccAbstract) == 0) {
			// abtract
			if((modifiers & ~(ExtraCompilerModifiers.AccVisibilityMASK | ClassFileConstants.AccStatic)) == 0) {
				keywords[count++] = Keywords.ABSTRACT;
			}

			// final
			if((modifiers & ClassFileConstants.AccFinal) == 0) {
				keywords[count++] = Keywords.FINAL;
			}

			// static
			if((modifiers & ClassFileConstants.AccStatic) == 0) {
				keywords[count++] = Keywords.STATIC;
			}

			boolean canBeField = true;
			boolean canBeMethod = true;
			boolean canBeType = true;
			if((modifiers & ClassFileConstants.AccNative) != 0
				|| (modifiers & ClassFileConstants.AccStrictfp) != 0
				|| (modifiers & ClassFileConstants.AccSynchronized) != 0) {
				canBeField = false;
				canBeType = false;
			}

			if((modifiers & ClassFileConstants.AccTransient) != 0
				|| (modifiers & ClassFileConstants.AccVolatile) != 0) {
				canBeMethod = false;
				canBeType = false;
			}

			if(canBeField) {
				// transient
				if((modifiers & ClassFileConstants.AccTransient) == 0) {
					keywords[count++] = Keywords.TRANSIENT;
				}

				// volatile
				if((modifiers & ClassFileConstants.AccVolatile) == 0) {
					keywords[count++] = Keywords.VOLATILE;
				}
			}

			if(canBeMethod) {
				// native
				if((modifiers & ClassFileConstants.AccNative) == 0) {
					keywords[count++] = Keywords.NATIVE;
				}

				// strictfp
				if((modifiers & ClassFileConstants.AccStrictfp) == 0) {
					keywords[count++] = Keywords.STRICTFP;
				}

				// synchronized
				if((modifiers & ClassFileConstants.AccSynchronized) == 0) {
					keywords[count++] = Keywords.SYNCHRONIZED;
				}
			}

			if(canBeType) {
				keywords[count++] = Keywords.CLASS;
				keywords[count++] = Keywords.INTERFACE;

				if((modifiers & ClassFileConstants.AccFinal) == 0) {
					keywords[count++] = Keywords.ENUM;
				}
			}
		} else {
			// class
			keywords[count++] = Keywords.CLASS;
			keywords[count++] = Keywords.INTERFACE;
		}
		System.arraycopy(keywords, 0, keywords = new char[count][], 0, count);

		findKeywords(token, keywords, false, false);
	}
	private void findLabels(char[] label, char[][] choices) {
		if(choices == null || choices.length == 0) return;

		int length = label.length;
		for (int i = 0; i < choices.length; i++) {
			if (length <= choices[i].length
				&& CharOperation.prefixEquals(label, choices[i], false /* ignore case */
			)){
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(label, choices[i]);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors

				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.LABEL_REF)) {
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.LABEL_REF, this.actualCompletionPosition);
					proposal.setName(choices[i]);
					proposal.setCompletion(choices[i]);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}

	// 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 exactMatch,
		ReferenceBinding receiverType) {

		ObjectVector newMethodsFound =  new ObjectVector();
		// 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()) {
                newMethodsFound.add(method);
                continue next;
            }

			if (this.options.checkDeprecation &&
					method.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(method.declaringClass))
				continue next;

			//		if (noVoidReturnType && method.returnType == BaseTypes.VoidBinding) continue next;
			if(method.isStatic()) continue next;

			if (!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 (isFailedMatch(methodName, method.selector))
					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)
						&& this.lookupEnvironment.methodVerifier().isMethodSubsignature(otherMethod, method)) {
					continue next;
				}
			}

			newMethodsFound.add(method);

			int length = method.parameters.length;
			char[][] parameterPackageNames = new char[length][];
			char[][] parameterFullTypeNames = new char[length][];

			for (int i = 0; i < length; i++) {
				TypeBinding type = method.parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterFullTypeNames[i] = type.qualifiedSourceName();
			}

			char[][] parameterNames = findMethodParameterNames(method, parameterFullTypeNames);

			if(method.typeVariables != null && method.typeVariables.length > 0) {
				char[][] excludedNames = findEnclosingTypeNames(scope);
				char[][] substituedParameterNames = substituteMethodTypeParameterNames(method.typeVariables, excludedNames);
				if(substituedParameterNames != null) {
					method = new ParameterizedMethodBinding(
								method.declaringClass,
								method,
								substituedParameterNames,
								scope.environment());
				}
			}

			StringBuffer completion = new StringBuffer(10);
			if (!exactMatch) {
				createMethod(method, parameterPackageNames, parameterFullTypeNames, parameterNames, scope, completion);
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			relevance += R_METHOD_OVERIDE;
			if(method.isAbstract()) relevance += R_ABSTRACT_METHOD;
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_DECLARATION)) {
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_DECLARATION, this.actualCompletionPosition);
				proposal.setBinding(method);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setDeclarationKey(method.declaringClass.computeUniqueKey());
				proposal.setSignature(getSignature(method));
				MethodBinding original = method.original();
				if(original != method) {
					proposal.setOriginalSignature(getSignature(original));
				}
				proposal.setKey(method.computeUniqueKey());
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterFullTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setCompletion(completion.toString().toCharArray());
				proposal.setName(method.selector);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
		}
		methodsFound.addAll(newMethodsFound);
	}

	// Helper method for findMethods(char[], TypeBinding[], ReferenceBinding, Scope, ObjectVector, boolean, boolean, boolean)
	private void findLocalMethods(
		char[] methodName,
		TypeBinding[] typeArgTypes,
		TypeBinding[] argTypes,
		MethodBinding[] methods,
		Scope scope,
		ObjectVector methodsFound,
		boolean onlyStaticMethods,
		boolean exactMatch,
		ReferenceBinding receiverType,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		boolean canBePrefixed,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		char[] castedReceiver,
		int receiverStart,
		int receiverEnd) {

		boolean completionOnReferenceExpressionName = invocationSite instanceof ReferenceExpression;
		ObjectVector newMethodsFound =  new ObjectVector();
		// 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 minTypeArgLength = typeArgTypes == null ? 0 : typeArgTypes.length;
		int minArgLength = argTypes == null ? 0 : argTypes.length;

		char[][] boostMatches = new char[0][];
		char[] parameterName = findParameterNameAtLocationFromAssistParent(new ObjectVector(), scope);
		if(parameterName.length > 0) {
			char[] nameForMethods = capitalize(parameterName);
			boostMatches = new char[][] { parameterName, CharOperation.concat(GET, nameForMethods),
					CharOperation.concat(IS, nameForMethods) };
		}

		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 (this.options.checkDeprecation &&
					method.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(method.declaringClass))
				continue next;

			//TODO (david) perhaps the relevance of a void method must be lesser than other methods
			//if (expectedTypesPtr > -1 && method.returnType == BaseTypes.VoidBinding) continue next;

			if (onlyStaticMethods && !method.isStatic()) continue next;

			if (this.options.checkVisibility
				&& !method.canBeSeenBy(receiverType, invocationSite, scope)) continue next;

			if(superCall && method.isAbstract()) {
				methodsFound.add(new Object[]{method, receiverType});
				continue next;
			}

			if (exactMatch) {
				if (!CharOperation.equals(methodName, method.selector, false /* ignore case */)) {
					continue next;
				}
			} else {
				if (methodLength > method.selector.length) continue next;
				if (isFailedMatch(methodName, method.selector)) {
					continue next;
				}
			}

			if (minTypeArgLength != 0 && minTypeArgLength != method.typeVariables.length)
				continue next;

			if (minTypeArgLength != 0) {
				method = scope.environment().createParameterizedGenericMethod(method, typeArgTypes);
			}

			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 (!argTypes[a].isCompatibleWith(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 && TypeBinding.equalsEquals(receiverType, otherReceiverType))
					continue next;

				if (CharOperation.equals(method.selector, otherMethod.selector, true)) {
					if (TypeBinding.equalsEquals(receiverType, otherReceiverType)) {
						if (this.lookupEnvironment.methodVerifier().isMethodSubsignature(otherMethod, method)) {
							if (!superCall || !otherMethod.declaringClass.isInterface()) {
								continue next;
							}
						}
					} else {
						if (this.lookupEnvironment.methodVerifier().isMethodSubsignature(otherMethod, method)) {
							if(receiverType.isAnonymousType()) continue next;

							if(!superCall) {
								if(!canBePrefixed) continue next;

								prefixRequired = true;
							}
						}
					}
				}
			}

			newMethodsFound.add(new Object[]{method, receiverType});

			ReferenceBinding superTypeWithSameErasure = (ReferenceBinding)receiverType.findSuperTypeOriginatingFrom(method.declaringClass);
			if (TypeBinding.notEquals(method.declaringClass, superTypeWithSameErasure)) {
				MethodBinding[] otherMethods = superTypeWithSameErasure.getMethods(method.selector);
				for (int i = 0; i < otherMethods.length; i++) {
					if(otherMethods[i].original() == method.original()) {
						method = otherMethods[i];
					}
				}
			}

			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.original().parameters[i];
					parameterPackageNames[i] = type.qualifiedPackageName();
					parameterTypeNames[i] = type.qualifiedSourceName();
				}
			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

			char[] completion = CharOperation.NO_CHAR;

			int previousStartPosition = this.startPosition;
			int previousTokenStart = this.tokenStart;

			// Special case for completion in javadoc
			if (this.assistNodeInJavadoc > 0) {
				Expression receiver = null;
				if (invocationSite instanceof CompletionOnJavadocMessageSend) {
					CompletionOnJavadocMessageSend msg = (CompletionOnJavadocMessageSend) invocationSite;
					receiver = msg.receiver;
				} else if (invocationSite instanceof CompletionOnJavadocFieldReference) {
					CompletionOnJavadocFieldReference fieldRef = (CompletionOnJavadocFieldReference) invocationSite;
					receiver = fieldRef.receiver;
				}
				if (receiver != null) {
					StringBuffer javadocCompletion = new StringBuffer();
					if (receiver.isThis()) {
						if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0) {
							javadocCompletion.append('#');
						}
					} else if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0) {
						if (receiver instanceof JavadocSingleTypeReference) {
							JavadocSingleTypeReference typeRef = (JavadocSingleTypeReference) receiver;
							javadocCompletion.append(typeRef.token);
							javadocCompletion.append('#');
						} else if (receiver instanceof JavadocQualifiedTypeReference) {
							JavadocQualifiedTypeReference typeRef = (JavadocQualifiedTypeReference) receiver;
							completion = CharOperation.concat(CharOperation.concatWith(typeRef.tokens, '.'), method.selector, '#');
							for (int t=0,nt =typeRef.tokens.length; t<nt; t++) {
								if (t>0) javadocCompletion.append('.');
								javadocCompletion.append(typeRef.tokens[t]);
							}
							javadocCompletion.append('#');
						}
					}
					javadocCompletion.append(method.selector);
					// Append parameters types
					javadocCompletion.append('(');
					if (method.parameters != null) {
						boolean isVarargs = method.isVarargs();
						for (int p=0, ln=method.parameters.length; p<ln; p++) {
							if (p>0) javadocCompletion.append(", "); //$NON-NLS-1$
							TypeBinding argTypeBinding = method.parameters[p];
							if (isVarargs && p == ln - 1)  {
								createVargsType(argTypeBinding.erasure(), scope, javadocCompletion);
							} else {
								createType(argTypeBinding.erasure(), scope,javadocCompletion);
							}
						}
					}
					javadocCompletion.append(')');
					completion = javadocCompletion.toString().toCharArray();
				}
			} else {
				// nothing to insert - do not want to replace the existing selector & arguments
				if (!exactMatch) {
					if (completionOnReferenceExpressionName)
						completion = method.selector;
					else if (this.source != null
						&& this.source.length > this.endPosition
						&& this.source[this.endPosition] == '(')
						completion = method.selector;
					else
						completion = CharOperation.concat(method.selector, new char[] { '(', ')' });

					if (castedReceiver != null) {
						completion = CharOperation.concat(castedReceiver, completion);
					}
				} else {
					if(prefixRequired && (this.source != null)) {
						completion = CharOperation.subarray(this.source, this.startPosition, this.endPosition);
					} else {
						this.startPosition = this.endPosition;
					}
					this.tokenStart = this.tokenEnd;
				}

				if(prefixRequired || this.options.forceImplicitQualification){
					char[] prefix = computePrefix(scope.enclosingSourceType(), invocationScope.enclosingSourceType(), method.isStatic());
					completion = CharOperation.concat(prefix,completion,'.');
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			if(boostMatches.length > 0) {
				relevance += computeRelevanceForCaseMatching(boostMatches, method.selector);
			}
			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(method.returnType);
			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
				continue;
			} else if(!this.strictMatchForExtepectedType) {
				relevance += computeRelevanceForExpectingType;
			}
			relevance += computeRelevanceForEnumConstant(method.returnType);
			relevance += computeRelevanceForStatic(onlyStaticMethods, method.isStatic());
			relevance += computeRelevanceForQualification(prefixRequired);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			if (onlyStaticMethods && this.insideQualifiedReference) {
				relevance += computeRelevanceForInheritance(receiverType, method.declaringClass);
			}
			if (missingElements != null) {
				relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
			}
			relevance += computeRelevanceForSuper(method, scope, invocationSite);
			this.noProposal = false;

			if (castedReceiver == null) {
				// Standard proposal
				if(!this.isIgnored(CompletionProposal.METHOD_REF, missingElements != null) && (this.assistNodeInJavadoc & CompletionOnJavadoc.ONLY_INLINE_TAG) == 0) {
					InternalCompletionProposal proposal =  createProposal(completionOnReferenceExpressionName ? CompletionProposal.METHOD_NAME_REFERENCE : CompletionProposal.METHOD_REF, this.actualCompletionPosition);
					proposal.setBinding(method);
					proposal.setDeclarationSignature(getSignature(method.declaringClass));
					proposal.setSignature(getSignature(method));
					MethodBinding original = method.original();
					if(original != method) {
						proposal.setOriginalSignature(getSignature(original));
					}
					proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
					proposal.setParameterPackageNames(parameterPackageNames);
					proposal.setParameterTypeNames(parameterTypeNames);
					proposal.setPackageName(method.returnType.qualifiedPackageName());
					proposal.setTypeName(method.returnType.qualifiedSourceName());
					proposal.setName(method.selector);
					if (missingElements != null) {
						CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
						for (int i = 0; i < missingElements.length; i++) {
							subProposals[i] =
								createRequiredTypeProposal(
										missingElements[i],
										missingElementsStarts[i],
										missingElementsEnds[i],
										relevance);
						}
						proposal.setRequiredProposals(subProposals);
					}
					proposal.setCompletion(completion);
					proposal.setFlags(method.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					if(parameterNames != null) proposal.setParameterNames(parameterNames);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}

				// Javadoc proposal
				if ((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_METHOD_REF)) {
					char[] javadocCompletion = inlineTagCompletion(completion, JavadocTagConstants.TAG_LINK);
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.JAVADOC_METHOD_REF, this.actualCompletionPosition);
					proposal.setBinding(method);
					proposal.setDeclarationSignature(getSignature(method.declaringClass));
					proposal.setSignature(getSignature(method));
					MethodBinding original = method.original();
					if(original != method) {
						proposal.setOriginalSignature(getSignature(original));
					}
					proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
					proposal.setParameterPackageNames(parameterPackageNames);
					proposal.setParameterTypeNames(parameterTypeNames);
					proposal.setPackageName(method.returnType.qualifiedPackageName());
					proposal.setTypeName(method.returnType.qualifiedSourceName());
					proposal.setName(method.selector);
					proposal.setCompletion(javadocCompletion);
					proposal.setFlags(method.modifiers);
					int start = (this.assistNodeInJavadoc & CompletionOnJavadoc.REPLACE_TAG) != 0 ? this.javadocTagPosition : this.startPosition;
					proposal.setReplaceRange(start - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance+R_INLINE_TAG);
					if(parameterNames != null) proposal.setParameterNames(parameterNames);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			} else {
				if(!this.isIgnored(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, missingElements != null)) {
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER, this.actualCompletionPosition);
					proposal.setBinding(method);
					proposal.setDeclarationSignature(getSignature(method.declaringClass));
					proposal.setSignature(getSignature(method));
					MethodBinding original = method.original();
					if(original != method) {
						proposal.setOriginalSignature(getSignature(original));
					}
					proposal.setReceiverSignature(getSignature(receiverType));
					proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
					proposal.setParameterPackageNames(parameterPackageNames);
					proposal.setParameterTypeNames(parameterTypeNames);
					proposal.setPackageName(method.returnType.qualifiedPackageName());
					proposal.setTypeName(method.returnType.qualifiedSourceName());
					proposal.setName(method.selector);
					if (missingElements != null) {
						CompletionProposal[] subProposals = new CompletionProposal[missingElements.length];
						for (int i = 0; i < missingElements.length; i++) {
							subProposals[i] =
								createRequiredTypeProposal(
										missingElements[i],
										missingElementsStarts[i],
										missingElementsEnds[i],
										relevance);
						}
						proposal.setRequiredProposals(subProposals);
					}
					proposal.setCompletion(completion);
					proposal.setFlags(method.modifiers);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setReceiverRange(receiverStart - this.offset, receiverEnd - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					if(parameterNames != null) proposal.setParameterNames(parameterNames);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
			this.startPosition = previousStartPosition;
			this.tokenStart = previousTokenStart;
		}

		methodsFound.addAll(newMethodsFound);
	}

	private char[] capitalize(char[] name) {
		char[] result = new char[name.length];
		for (int i = 0; i < name.length; i++) {
			if (i == 0) {
				result[i] = Character.toUpperCase(name[i]);
			} else {
				result[i] = name[i];
			}
		}
		return result;
	}

	private char[] findParameterNameAtLocationFromAssistParent(ObjectVector methodsFound, Scope scope) {
		char[] parameterName = new char[0];
		MethodBinding[] candidates;
		final Expression[] arguments;
		if (this.parser.assistNodeParent instanceof MessageSend ms) {
			arguments = ms.arguments;
			candidates = Optional.ofNullable(ms.actualReceiverType)
					.or(() -> Optional.ofNullable(ms.receiver).map(r -> r.resolvedType))
					.map(t -> t.getMethods(ms.selector)).orElse(new MethodBinding[0]);
		} else if (this.parser.assistNode instanceof MessageSend ms) {
			arguments = ms.arguments;
			candidates = StreamSupport.stream(methodsFound.spliterator(), false)
					.filter(Objects::nonNull)
					.filter(o -> o instanceof Object[]).map(o -> (Object[]) o)
					.map(o -> o[0]).filter(o -> o instanceof MethodBinding).map(b -> (MethodBinding) b)
					.filter(b -> CharOperation.equals(ms.selector, b.selector)).findFirst()
					.map(m -> new MethodBinding[] { m }).orElse(new MethodBinding[0]);
		} else if (this.parser.assistNodeParent instanceof AllocationExpression ae) {
			arguments = ae.arguments;
			if (ae.type != null && ae.type.resolvedType instanceof ReferenceBinding rb) {
				findConstructors(rb, computeTypes(arguments), scope, ae, false, null, null, null, false, methodsFound,
						true);
			}
			candidates = StreamSupport.stream(methodsFound.spliterator(), false).filter(Objects::nonNull)
					.filter(o -> o instanceof Object[]).map(o -> (Object[]) o).map(o -> o[0])
					.filter(o -> o instanceof MethodBinding).map(b -> (MethodBinding) b).filter(b -> b.isConstructor())
					.toArray(MethodBinding[]::new);
		} else {
			return parameterName;
		}

		final int argumentLength = (arguments != null) ? arguments.length : 1;
		for (MethodBinding binding : candidates) {
			if (binding.parameterNames.length >= argumentLength) {
				for (int i = 0; i < argumentLength; i++) {
					if (arguments == null || arguments[i] == this.parser.assistNode) {
						parameterName = binding.parameterNames[i];
					}
				}
			}
			if (parameterName.length > 0) {
				break;
			}
		}
		return parameterName;
	}
	private void findLocalMethodsFromFavorites(
			char[] methodName,
			MethodBinding[] methods,
			Scope scope,
			ObjectVector methodsFound,
			ObjectVector methodsFoundFromFavorites,
			ReferenceBinding receiverType,
			InvocationSite invocationSite,
			Scope invocationScope) {

			char[] typeName = CharOperation.concatWith(receiverType.compoundName, '.');

			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 (this.options.checkDeprecation &&
						method.isViewedAsDeprecated() &&
						!scope.isDefinedInSameUnit(method.declaringClass))
					continue next;

				if (!method.isStatic()) continue next;

				if (this.options.checkVisibility
					&& !method.canBeSeenBy(receiverType, invocationSite, scope)) continue next;

				if (methodLength > method.selector.length) continue next;

				if (isFailedMatch(methodName, method.selector)) {
					continue next;
				}

				for (int i = methodsFoundFromFavorites.size; --i >= 0;) {
					Object[] other = (Object[]) methodsFoundFromFavorites.elementAt(i);
					MethodBinding otherMethod = (MethodBinding) other[0];

					if (method == otherMethod) continue next;

					if (CharOperation.equals(method.selector, otherMethod.selector, true)) {
						if (TypeBinding.equalsEquals(otherMethod.declaringClass, method.declaringClass) &&
								this.lookupEnvironment.methodVerifier().isMethodSubsignature(otherMethod, method)) {
							continue next;
						}
					}
				}

				for (int i = methodsFound.size; --i >= 0;) {
					Object[] other = (Object[]) methodsFound.elementAt(i);
					MethodBinding otherMethod = (MethodBinding) other[0];

					if (method == otherMethod) continue next;

					if (CharOperation.equals(method.selector, otherMethod.selector, true)) {
						if (this.lookupEnvironment.methodVerifier().isMethodSubsignature(otherMethod, method)) {
							continue next;
						}
					}
				}

				boolean proposeStaticImport = !(this.compilerOptions.complianceLevel < ClassFileConstants.JDK1_5) &&
					this.options.suggestStaticImport;

				boolean isAlreadyImported = false;
				if (!proposeStaticImport) {
					if(!this.importCachesInitialized) {
						initializeImportCaches();
					}
					for (int j = 0; j < this.importCacheCount; j++) {
						char[][] importName = this.importsCache[j];
						if(CharOperation.equals(receiverType.sourceName, importName[0])) {
							if (!CharOperation.equals(typeName, importName[1])) {
								continue next;
							} else {
								isAlreadyImported = true;
							}
						}
					}
				}

				methodsFoundFromFavorites.add(new Object[]{method, receiverType});

				ReferenceBinding superTypeWithSameErasure = (ReferenceBinding)receiverType.findSuperTypeOriginatingFrom(method.declaringClass);
				if (TypeBinding.notEquals(method.declaringClass, superTypeWithSameErasure)) {
					MethodBinding[] otherMethods = superTypeWithSameErasure.getMethods(method.selector);
					for (int i = 0; i < otherMethods.length; i++) {
						if(otherMethods[i].original() == method.original()) {
							method = otherMethods[i];
						}
					}
				}

				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.original().parameters[i];
					parameterPackageNames[i] = type.qualifiedPackageName();
					parameterTypeNames[i] = type.qualifiedSourceName();
				}
				char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

				char[] completion = CharOperation.NO_CHAR;

				int previousStartPosition = this.startPosition;
				int previousTokenStart = this.tokenStart;

				if (this.source != null
					&& this.source.length > this.endPosition
					&& this.source[this.endPosition] == '(') {
					completion = method.selector;
				} else {
					completion = CharOperation.concat(method.selector, new char[] { '(', ')' });
				}

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(methodName, method.selector);
				int computeRelevanceForExpectingType = computeRelevanceForExpectingType(method.returnType);
				if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
					continue;
				} else if(!this.strictMatchForExtepectedType) {
					relevance += computeRelevanceForExpectingType;
				}

				relevance += computeRelevanceForEnumConstant(method.returnType);
				relevance += computeRelevanceForStatic(true, method.isStatic());
				relevance += computeRelevanceForQualification(true);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

				CompilationUnitDeclaration cu = this.unitScope.referenceContext;
				int importStart = cu.types[0].declarationSourceStart;
				int importEnd = importStart;

				this.noProposal = false;

				if (!proposeStaticImport) {
					if (isAlreadyImported) {
						if (!isIgnored(CompletionProposal.METHOD_REF)) {
							completion = CharOperation.concat(receiverType.sourceName, completion, '.');

							InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
							proposal.setBinding(method);
							proposal.setDeclarationSignature(getSignature(method.declaringClass));
							proposal.setSignature(getSignature(method));
							MethodBinding original = method.original();
							if(original != method) {
								proposal.setOriginalSignature(getSignature(original));
							}
							proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
							proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
							proposal.setParameterPackageNames(parameterPackageNames);
							proposal.setParameterTypeNames(parameterTypeNames);
							proposal.setPackageName(method.returnType.qualifiedPackageName());
							proposal.setTypeName(method.returnType.qualifiedSourceName());
							proposal.setName(method.selector);
							proposal.setCompletion(completion);
							proposal.setFlags(method.modifiers);
							proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
							proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
							proposal.setRelevance(relevance);
							if(parameterNames != null) proposal.setParameterNames(parameterNames);

							this.requestor.accept(proposal);
							if(DEBUG) {
								this.printDebug(proposal);
							}
						}
					} else if (!this.isIgnored(CompletionProposal.METHOD_REF, CompletionProposal.TYPE_IMPORT)) {
						completion = CharOperation.concat(receiverType.sourceName, completion, '.');

						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
						proposal.setBinding(method);
						proposal.setDeclarationSignature(getSignature(method.declaringClass));
						proposal.setSignature(getSignature(method));
						MethodBinding original = method.original();
						if(original != method) {
							proposal.setOriginalSignature(getSignature(original));
						}
						proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
						proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
						proposal.setParameterPackageNames(parameterPackageNames);
						proposal.setParameterTypeNames(parameterTypeNames);
						proposal.setPackageName(method.returnType.qualifiedPackageName());
						proposal.setTypeName(method.returnType.qualifiedSourceName());
						proposal.setName(method.selector);
						proposal.setCompletion(completion);
						proposal.setFlags(method.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						if(parameterNames != null) proposal.setParameterNames(parameterNames);

						char[] typeImportCompletion = createImportCharArray(typeName, false, false);

						InternalCompletionProposal typeImportProposal = createProposal(CompletionProposal.TYPE_IMPORT, this.actualCompletionPosition);
						typeImportProposal.nameLookup = this.nameEnvironment.nameLookup;
						typeImportProposal.completionEngine = this;
						char[] packageName = receiverType.qualifiedPackageName();
						typeImportProposal.setDeclarationSignature(packageName);
						typeImportProposal.setSignature(getSignature(receiverType));
						typeImportProposal.setPackageName(packageName);
						typeImportProposal.setTypeName(receiverType.qualifiedSourceName());
						typeImportProposal.setCompletion(typeImportCompletion);
						typeImportProposal.setFlags(receiverType.modifiers);
						typeImportProposal.setAdditionalFlags(CompletionFlags.Default);
						typeImportProposal.setReplaceRange(importStart - this.offset, importEnd - this.offset);
						typeImportProposal.setTokenRange(importStart - this.offset, importEnd - this.offset);
						typeImportProposal.setRelevance(relevance);

						proposal.setRequiredProposals(new CompletionProposal[]{typeImportProposal});

						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				} else {
					if (!this.isIgnored(CompletionProposal.METHOD_REF, CompletionProposal.METHOD_IMPORT)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
						proposal.setBinding(method);
						proposal.setDeclarationSignature(getSignature(method.declaringClass));
						proposal.setSignature(getSignature(method));
						MethodBinding original = method.original();
						if(original != method) {
							proposal.setOriginalSignature(getSignature(original));
						}
						proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
						proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
						proposal.setParameterPackageNames(parameterPackageNames);
						proposal.setParameterTypeNames(parameterTypeNames);
						proposal.setPackageName(method.returnType.qualifiedPackageName());
						proposal.setTypeName(method.returnType.qualifiedSourceName());
						proposal.setName(method.selector);
						proposal.setCompletion(completion);
						proposal.setFlags(method.modifiers);
						proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						if(parameterNames != null) proposal.setParameterNames(parameterNames);

						char[] methodImportCompletion = createImportCharArray(CharOperation.concat(typeName, method.selector, '.'), true, false);

						InternalCompletionProposal methodImportProposal = createProposal(CompletionProposal.METHOD_IMPORT, this.actualCompletionPosition);
						methodImportProposal.setDeclarationSignature(getSignature(method.declaringClass));
						methodImportProposal.setSignature(getSignature(method));
						if(original != method) {
							proposal.setOriginalSignature(getSignature(original));
						}
						methodImportProposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
						methodImportProposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
						methodImportProposal.setParameterPackageNames(parameterPackageNames);
						methodImportProposal.setParameterTypeNames(parameterTypeNames);
						methodImportProposal.setPackageName(method.returnType.qualifiedPackageName());
						methodImportProposal.setTypeName(method.returnType.qualifiedSourceName());
						methodImportProposal.setName(method.selector);
						methodImportProposal.setCompletion(methodImportCompletion);
						methodImportProposal.setFlags(method.modifiers);
						methodImportProposal.setAdditionalFlags(CompletionFlags.StaticImport);
						methodImportProposal.setReplaceRange(importStart - this.offset, importEnd - this.offset);
						methodImportProposal.setTokenRange(importStart - this.offset, importEnd - this.offset);
						methodImportProposal.setRelevance(relevance);
						if(parameterNames != null) methodImportProposal.setParameterNames(parameterNames);

						proposal.setRequiredProposals(new CompletionProposal[]{methodImportProposal});

						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}

				this.startPosition = previousStartPosition;
				this.tokenStart = previousTokenStart;
			}
		}

	/**
	 * Helper method for findMethods(char[], TypeBinding[], ReferenceBinding, Scope, ObjectVector, boolean, boolean, boolean)
	 * Note that the method doesn't do a comparison of the method names and expects the client to handle the same.
	 *
	 * @methodName method as entered by the user, the one to completed
	 * @param methods a resultant array of MethodBinding, whose names should match methodName. The calling client must ensure that this check is handled.
	 */
	private void findLocalMethodsFromStaticImports(
		char[] methodName,
		MethodBinding[] methods,
		Scope scope,
		boolean exactMatch,
		ObjectVector methodsFound,
		ReferenceBinding receiverType,
		InvocationSite invocationSite) {

		ObjectVector newMethodsFound =  new ObjectVector();

		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.isStatic()) continue next;

			if (this.options.checkDeprecation &&
					method.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(method.declaringClass))
				continue next;

			if (this.options.checkVisibility
				&& !method.canBeSeenBy(receiverType, invocationSite, scope)) continue next;

			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 && TypeBinding.equalsEquals(receiverType, otherReceiverType))
					continue next;

				if (CharOperation.equals(method.selector, otherMethod.selector, true)) {
					if (this.lookupEnvironment.methodVerifier().isMethodSubsignature(otherMethod, method)) {
						continue next;
					}
				}
			}

			newMethodsFound.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.original().parameters[i];
				parameterPackageNames[i] = type.qualifiedPackageName();
				parameterTypeNames[i] = type.qualifiedSourceName();
			}
			char[][] parameterNames = findMethodParameterNames(method,parameterTypeNames);

			char[] completion = CharOperation.NO_CHAR;

			int previousStartPosition = this.startPosition;
			int previousTokenStart = this.tokenStart;

			if (!exactMatch) {
				if (this.source != null
					&& this.source.length > this.endPosition
					&& this.source[this.endPosition] == '(') {
					completion = method.selector;
				} else {
					completion = CharOperation.concat(method.selector, new char[] { '(', ')' });
				}
			} else {
				this.startPosition = this.endPosition;
				this.tokenStart = this.tokenEnd;
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(methodName, method.selector);
			int computeRelevanceForExpectingType = computeRelevanceForExpectingType(method.returnType);
			if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
				continue;
			} else if(!this.strictMatchForExtepectedType) {
				relevance += computeRelevanceForExpectingType;
			}

			int computeRelevanceForEnumConstant = computeRelevanceForEnumConstant(method.returnType);
			if(this.strictMatchForExtepectedType && computeRelevanceForEnumConstant <= 0) {
				continue;
			} else if(!this.strictMatchForExtepectedType) {
				relevance += computeRelevanceForEnumConstant;
			}

			relevance += computeRelevanceForStatic(true, method.isStatic());
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

			this.noProposal = false;
			if(!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
				InternalCompletionProposal proposal =  createProposal(CompletionProposal.METHOD_REF, this.actualCompletionPosition);
				proposal.setBinding(method);
				proposal.setDeclarationSignature(getSignature(method.declaringClass));
				proposal.setSignature(getSignature(method));
				MethodBinding original = method.original();
				if(original != method) {
					proposal.setOriginalSignature(getSignature(original));
				}
				proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
				proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
				proposal.setParameterPackageNames(parameterPackageNames);
				proposal.setParameterTypeNames(parameterTypeNames);
				proposal.setPackageName(method.returnType.qualifiedPackageName());
				proposal.setTypeName(method.returnType.qualifiedSourceName());
				proposal.setName(method.selector);
				proposal.setCompletion(completion);
				proposal.setFlags(method.modifiers);
				proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
				proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
				proposal.setRelevance(relevance);
				if(parameterNames != null) proposal.setParameterNames(parameterNames);
				this.requestor.accept(proposal);
				if(DEBUG) {
					this.printDebug(proposal);
				}
			}
			this.startPosition = previousStartPosition;
			this.tokenStart = previousTokenStart;
		}

		methodsFound.addAll(newMethodsFound);
	}

	private void findLocalMethodsFromStaticImports(
			char[] token,
			Scope scope,
			InvocationSite invocationSite,
			Scope invocationScope,
			boolean exactMatch,
			ObjectVector methodsFound,
			boolean proposeMethod) {
		findFieldsAndMethodsFromStaticImports(
				token,
				scope,
				invocationSite,
				invocationScope,
				exactMatch,
				false,
				new ObjectVector(),
				new ObjectVector(),
				methodsFound,
				false,
				proposeMethod);
	}
	protected void findMembers(
			char[] token,
			ReferenceBinding receiverType,
			Scope scope,
			InvocationSite invocationSite,
			boolean isInsideAnnotationAttribute,
			Binding[] missingElements,
			int[] missingElementsStarts,
			int[] missingElementsEnds,
			boolean missingElementsHaveProblems) {

		if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			findMemberTypes(
					token,
					receiverType,
					scope,
					scope.enclosingSourceType(),
					false,
					true,
					new ObjectVector(),
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems);
		}
		if (!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
			findClassField(
					token,
					receiverType,
					scope,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems);
		}

		MethodScope methodScope = null;
		if (!isInsideAnnotationAttribute &&
				!this.requestor.isIgnored(CompletionProposal.KEYWORD) &&
				((scope instanceof MethodScope && !((MethodScope)scope).isStatic)
				|| ((methodScope = scope.enclosingMethodScope()) != null && !methodScope.isStatic))) {
			if (token.length >= 0) {
				boolean isInterface = false;
				if (receiverType != null) {
					isInterface = receiverType.isInterface();
				}
				if (!isInterface) {
					if (hasCompatibleEnclosing(scope, receiverType)) {
						findKeywords(token, new char[][] { Keywords.THIS, Keywords.SUPER }, true, false);
					}
				} else {
					boolean isEqual = false;
					char[] enclosingSourceName = null;
					if(scope.enclosingSourceType() != null)
						enclosingSourceName = scope.enclosingSourceType().sourceName;
					char[] receiverSourceName = null;
					if (receiverType != null) {
						receiverSourceName = receiverType.sourceName;
					}
					if( enclosingSourceName !=null & receiverSourceName !=null)
						isEqual = Arrays.equals(enclosingSourceName, receiverSourceName);
					if(isEqual) {
						findKeywords(token, new char[][] { Keywords.THIS }, true, false);
					} else {
						// Check if the enclosing source implements this interface then show super
						if (scope.enclosingSourceType() != null) {
							SourceTypeBinding src = scope.enclosingSourceType();
							ReferenceBinding[] superInterfaces = src.superInterfaces();
							boolean implemented = false;
							for (ReferenceBinding referenceBinding : superInterfaces) {
								if (Arrays.equals(referenceBinding.sourceName, receiverSourceName)) {
									implemented = true;
									break;
								}
							}
							if (implemented) {
								findKeywords(token, new char[][] { Keywords.SUPER }, true, false);
							}
						}
					}
				}
			}
		}

		if (!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
			findFields(
				token,
				receiverType,
				scope,
				new ObjectVector(),
				new ObjectVector(),
				true,
				invocationSite,
				scope,
				false,
				false,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				null,
				-1,
				-1);
		}

		if (!isInsideAnnotationAttribute && !this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
			findMethods(
				token,
				null,
				null,
				receiverType,
				scope,
				new ObjectVector(),
				true,
				false,
				invocationSite,
				scope,
				false,
				false,
				false,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems,
				null,
				-1,
				-1);
		}
	}

	private boolean hasCompatibleEnclosing(Scope scope, ReferenceBinding receiverType) {
		ReferenceBinding enclosing = scope.enclosingSourceType();
		while (enclosing != null) {
			if (enclosing.isCompatibleWith(receiverType, scope))
				return true;
			enclosing = enclosing.enclosingType();
		}
		return false;
	}

	private void findMembersFromMissingType(
			final char[] token,
			final long pos,
			TypeBinding resolveType,
			final Scope scope,
			final InvocationSite invocationSite,
			final boolean isInsideAnnotationAttribute) {
		MissingTypesGuesser missingTypesConverter = new MissingTypesGuesser(this);
		MissingTypesGuesser.GuessedTypeRequestor substitutionRequestor =
			new MissingTypesGuesser.GuessedTypeRequestor() {
				@Override
				public void accept(
						TypeBinding guessedType,
						Binding[] missingElements,
						int[] missingElementsStarts,
						int[] missingElementsEnds,
						boolean hasProblems) {
					if (guessedType instanceof ReferenceBinding) {
						findMembers(
								CompletionEngine.this.completionToken,
								(ReferenceBinding)guessedType,
								scope,
								invocationSite,
								isInsideAnnotationAttribute,
								missingElements,
								missingElementsStarts,
								missingElementsEnds,
								hasProblems);
					}
				}
			};
		SingleTypeReference typeRef = new SingleTypeReference(token, pos);
		typeRef.resolvedType = new ProblemReferenceBinding(new char[][]{ token }, null, ProblemReasons.NotFound);
		missingTypesConverter.guess(typeRef, scope, substitutionRequestor);
	}

	private void findMemberTypes(
		char[] typeName,
		ReferenceBinding receiverType,
		Scope scope,
		SourceTypeBinding typeInvocation,
		boolean staticOnly,
		boolean staticFieldsAndMethodOnly,
		boolean fromStaticImport,
		boolean checkQualification,
		boolean proposeAllMemberTypes,
		SourceTypeBinding typeToIgnore,
		ObjectVector typesFound,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems) {

		ReferenceBinding currentType = receiverType;
		if (typeName == null)
			return;

		if (this.insideQualifiedReference
			|| typeName.length == 0) { // do not search up the hierarchy

			findMemberTypes(
				typeName,
				currentType.memberTypes(),
				typesFound,
				receiverType,
				typeInvocation,
				staticOnly,
				staticFieldsAndMethodOnly,
				fromStaticImport,
				checkQualification,
				scope,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems);
			return;
		}

		ReferenceBinding[] interfacesToVisit = null;
		int nextPosition = 0;

		do {
			ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
			if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
				if (interfacesToVisit == null) {
					interfacesToVisit = itsInterfaces;
					nextPosition = interfacesToVisit.length;
				} else {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}

			findMemberTypes(
				typeName,
				currentType.memberTypes(),
				typesFound,
				receiverType,
				typeInvocation,
				staticOnly,
				staticFieldsAndMethodOnly,
				fromStaticImport,
				checkQualification,
				scope,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems);

			currentType = currentType.superclass();
		} while (currentType != null);

		if(proposeAllMemberTypes) {
			ReferenceBinding[] memberTypes = receiverType.memberTypes();
			for (int i = 0; i < memberTypes.length; i++) {
				if(TypeBinding.notEquals(memberTypes[i], typeToIgnore)) {
					findSubMemberTypes(
						typeName,
						memberTypes[i],
						scope,
						typeInvocation,
						staticOnly,
						staticFieldsAndMethodOnly,
						fromStaticImport,
						typesFound);
				}
			}
		}

		if (interfacesToVisit != null) {
			for (int i = 0; i < nextPosition; i++) {
				ReferenceBinding anInterface = interfacesToVisit[i];
				findMemberTypes(
					typeName,
					anInterface.memberTypes(),
					typesFound,
					receiverType,
					typeInvocation,
					staticOnly,
					staticFieldsAndMethodOnly,
					fromStaticImport,
					checkQualification,
					scope,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems);

				ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
				if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
					int itsLength = itsInterfaces.length;
					if (nextPosition + itsLength >= interfacesToVisit.length)
						System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
					nextInterface : for (int a = 0; a < itsLength; a++) {
						ReferenceBinding next = itsInterfaces[a];
						for (int b = 0; b < nextPosition; b++)
							if (TypeBinding.equalsEquals(next, interfacesToVisit[b])) continue nextInterface;
						interfacesToVisit[nextPosition++] = next;
					}
				}
			}
		}
	}

	protected void findMemberTypes(
		char[] typeName,
		ReferenceBinding receiverType,
		Scope scope,
		SourceTypeBinding typeInvocation,
		boolean staticOnly,
		boolean staticFieldsAndMethodOnly,
		ObjectVector typesFound,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems)  {
		findMemberTypes(
				typeName,
				receiverType,
				scope,
				typeInvocation,
				staticOnly,
				staticFieldsAndMethodOnly,
				false,
				false,
				false,
				null,
				typesFound,
				missingElements,
				missingElementsStarts,
				missingElementsEnds,
				missingElementsHaveProblems);
	}
		// Helper method for findMemberTypes(char[], ReferenceBinding, Scope)
	private void findMemberTypes(
		char[] typeName,
		ReferenceBinding[] memberTypes,
		ObjectVector typesFound,
		ReferenceBinding receiverType,
		SourceTypeBinding invocationType,
		boolean staticOnly,
		boolean staticFieldsAndMethodOnly,
		boolean fromStaticImport,
		boolean checkQualification,
		Scope scope,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems) {

		// 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 (staticOnly && !memberType.isStatic()) continue next;

			if (isForbidden(memberType)) continue next;

			if (typeLength > memberType.sourceName.length)
				continue next;

			if (isFailedMatch(typeName, memberType.sourceName))
				continue next;

			if (this.options.checkDeprecation &&
					memberType.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(memberType))
				continue next;

			if (this.options.checkVisibility) {
				if (invocationType != null && !memberType.canBeSeenBy(receiverType, invocationType)) {
					continue next;
				} else if(invocationType == null && !memberType.canBeSeenBy(this.unitScope.fPackage)) {
					continue next;
				}
			}

			if (this.insideQualifiedReference &&
					receiverType.isParameterizedType() &&
					memberType.isStatic()) {
				continue next;
			}

			for (int i = typesFound.size; --i >= 0;) {
				ReferenceBinding otherType = (ReferenceBinding) typesFound.elementAt(i);

				if (TypeBinding.equalsEquals(memberType, otherType))
					continue next;

				if (CharOperation.equals(memberType.sourceName, otherType.sourceName, true)
						&& otherType.isNestedType()) {

					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);

			if (this.assistNodeIsExtendedType && memberType.isFinal()) continue next;
			if (this.assistNodeIsInterfaceExcludingAnnotation && memberType.isAnnotationType()) continue next;
			if(!this.insideQualifiedReference) {
				if(this.assistNodeIsClass || this.assistNodeIsException) {
					if(!memberType.isClass()) continue next;
				} else if(this.assistNodeIsInterface) {
					if(!memberType.isInterface() && !memberType.isAnnotationType()) continue next;
				} else if (this.assistNodeIsAnnotation) {
					if(!memberType.isAnnotationType()) continue next;
				}
			}

			char[] completionName = memberType.sourceName();

			boolean isQualified = false;
			if(checkQualification && !fromStaticImport) {
				char[] memberPackageName = memberType.qualifiedPackageName();
				char[] memberTypeName = memberType.sourceName();
				char[] memberEnclosingTypeNames = memberType.enclosingType().qualifiedSourceName();
				if (mustQualifyType(memberPackageName, memberTypeName, memberEnclosingTypeNames, memberType.modifiers)) {
					if (memberPackageName == null || memberPackageName.length == 0)
						if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
							break next; // ignore types from the default package from outside it
					isQualified = true;
					completionName =
						CharOperation.concat(
								memberPackageName,
								CharOperation.concat(
										memberEnclosingTypeNames,
										memberTypeName,
										'.'),
								'.');
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal(memberType);
			relevance += computeRelevanceForCaseMatching(typeName, memberType.sourceName);
			relevance += computeRelevanceForExpectingType(memberType);
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);
			if(!this.insideQualifiedReference) {
				relevance += computeRelevanceForQualification(isQualified);
			}
			if (staticFieldsAndMethodOnly && this.insideQualifiedReference) relevance += R_NON_INHERITED; // This criterion doesn't concern types and is added to be balanced with field and method relevance.

			if (memberType.isAnnotationType()) {
				relevance += computeRelevanceForAnnotation();
				relevance += computeRelevanceForAnnotationTarget(memberType);
			} else if (memberType.isClass()) {
				relevance += computeRelevanceForClass();
				relevance += computeRelevanceForException(memberType.sourceName);
			} else if(memberType.isEnum()) {
				relevance += computeRelevanceForEnum();
			} else if(memberType.isInterface()) {
				relevance += computeRelevanceForInterface();
			}

			if (missingElements != null) {
				relevance += computeRelevanceForMissingElements(missingElementsHaveProblems);
			}

			boolean allowingLongComputationProposals = isAllowingLongComputationProposals();

			this.noProposal = false;
			if (!this.assistNodeIsConstructor ||
					!allowingLongComputationProposals ||
					hasStaticMemberTypes(memberType, invocationType, this.unitScope) ||
					(memberType instanceof SourceTypeBinding && hasMemberTypesInEnclosingScope((SourceTypeBinding)memberType, scope)) ||
					hasArrayTypeAsExpectedSuperTypes()) {
				createTypeProposal(
						memberType,
						memberType.qualifiedSourceName(),
						IAccessRule.K_ACCESSIBLE,
						completionName,
						relevance,
						missingElements,
						missingElementsStarts,
						missingElementsEnds,
						missingElementsHaveProblems);
			}

			if (this.assistNodeIsConstructor && allowingLongComputationProposals) {
				findConstructorsOrAnonymousTypes(
						memberType,
						scope,
						FakeInvocationSite,
						isQualified,
						relevance);
			}
		}
	}
	private void findMemberTypesFromMissingType(
			char[] typeName,
			final long pos,
			final Scope scope)  {
		MissingTypesGuesser missingTypesConverter = new MissingTypesGuesser(this);
		MissingTypesGuesser.GuessedTypeRequestor substitutionRequestor =
			new MissingTypesGuesser.GuessedTypeRequestor() {
				@Override
				public void accept(
						TypeBinding guessedType,
						Binding[] missingElements,
						int[] missingElementsStarts,
						int[] missingElementsEnds,
						boolean hasProblems) {
					if (guessedType instanceof ReferenceBinding) {
						findMemberTypes(
								CompletionEngine.this.completionToken,
								(ReferenceBinding)guessedType,
								scope,
								scope.enclosingSourceType(),
								false,
								false,
								new ObjectVector(),
								missingElements,
								missingElementsStarts,
								missingElementsEnds,
								hasProblems);
					}
				}
			};
		SingleTypeReference typeRef = new SingleTypeReference(typeName, pos);
		typeRef.resolvedType = new ProblemReferenceBinding(new char[][]{ typeName }, null, ProblemReasons.NotFound);
		missingTypesConverter.guess(typeRef, scope, substitutionRequestor);
	}

	private void findMemberTypesFromMissingType(
			TypeReference typeRef,
			final long pos,
			final Scope scope)  {
		MissingTypesGuesser missingTypesConverter = new MissingTypesGuesser(this);
		MissingTypesGuesser.GuessedTypeRequestor substitutionRequestor =
			new MissingTypesGuesser.GuessedTypeRequestor() {
				@Override
				public void accept(
						TypeBinding guessedType,
						Binding[] missingElements,
						int[] missingElementsStarts,
						int[] missingElementsEnds,
						boolean hasProblems) {
					if (guessedType instanceof ReferenceBinding) {
						findMemberTypes(
								CompletionEngine.this.completionToken,
								(ReferenceBinding)guessedType,
								scope,
								scope.enclosingSourceType(),
								false,
								false,
								new ObjectVector(),
								missingElements,
								missingElementsStarts,
								missingElementsEnds,
								hasProblems);
					}
				}
			};
		missingTypesConverter.guess(typeRef, scope, substitutionRequestor);
	}

	private void findMethodDeclarations(
		char[] selector,
		ReferenceBinding receiverType,
		Scope scope,
		ObjectVector methodsFound,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems) {

		if (selector == null) {
			return;
		}

		MethodBinding[] receiverTypeMethods = receiverType.availableMethods();
		if (receiverTypeMethods != null){
			for (int i = 0; i < receiverTypeMethods.length; i++) {
				if(!receiverTypeMethods[i].isDefaultAbstract()) {
					methodsFound.add(receiverTypeMethods[i]);
				}
			}
		}

		ReferenceBinding currentType = receiverType;

		findInterfacesMethodDeclarations(
			selector,
			receiverType,
			currentType.superInterfaces(),
			scope,
			methodsFound,
			missingElements,
			missingElementsStarts,
			missingElementsEnds,
			missingElementsHaveProblems);

		if (receiverType.isInterface()) {
			currentType = scope.getJavaLangObject();
		} else {
			currentType = receiverType.superclass();
		}

		boolean hasPotentialDefaultAbstractMethods = true;
		boolean java8Plus = this.compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8;
		while (currentType != null) {

			MethodBinding[] methods = currentType.availableMethods();
			if (methods != null) {
				findLocalMethodDeclarations(
					selector,
					methods,
					scope,
					methodsFound,
					false,
					receiverType);
			}

			if (hasPotentialDefaultAbstractMethods && (java8Plus ||
					(currentType.isAbstract() ||
							currentType.isTypeVariable() ||
							currentType.isIntersectionType() ||
							currentType.isEnum()))){

				ReferenceBinding[] superInterfaces = currentType.superInterfaces();

				findInterfacesMethodDeclarations(
					selector,
					receiverType,
					superInterfaces,
					scope,
					methodsFound,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems);
			} else {
				hasPotentialDefaultAbstractMethods = false;
			}
			currentType = currentType.superclass();
		}
	}

	private char[][] findMethodParameterNames(MethodBinding method, char[][] parameterTypeNames){
		TypeBinding erasure =  method.declaringClass.erasure();
		if(!(erasure instanceof ReferenceBinding)) return null;

		char[][] parameterNames = null;

		int length = parameterTypeNames.length;

		if (length == 0){
			return CharOperation.NO_CHAR_CHAR;
		}
		// look into the corresponding unit if it is available
		if (erasure instanceof SourceTypeBinding){
			SourceTypeBinding sourceType = (SourceTypeBinding) erasure;

			if (sourceType.scope != null){
				TypeDeclaration parsedType;

				if ((parsedType = sourceType.scope.referenceContext) != null){
					AbstractMethodDeclaration methodDecl = parsedType.declarationOf(method.original());

					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){

			ReferenceBinding bindingType = (ReferenceBinding)erasure;

			char[] compoundName = CharOperation.concatWith(bindingType.compoundName, '.');
			Object type = this.typeCache.get(compoundName);

			ISourceType sourceType = null;
			if(type != null) {
				if(type instanceof ISourceType) {
					sourceType = (ISourceType) type;
				}
			} else {
				NameEnvironmentAnswer answer = this.nameEnvironment.findTypeInModules(bindingType.compoundName, this.unitScope.module());
				if(answer != null && answer.isSourceType()) {
					sourceType = answer.getSourceTypes()[0];
					this.typeCache.put(compoundName, sourceType);
				}
			}

			if(sourceType != null) {
				IType typeHandle = ((SourceTypeElementInfo) sourceType).getHandle();

				String[] parameterTypeSignatures = new String[length];
				for (int i = 0; i < length; i++) {
					parameterTypeSignatures[i] = Signature.createTypeSignature(parameterTypeNames[i], false);
				}
				IMethod searchedMethod = typeHandle.getMethod(String.valueOf(method.selector), parameterTypeSignatures);
				IMethod[] foundMethods = typeHandle.findMethods(searchedMethod);

				if(foundMethods != null) {
					int len = foundMethods.length;
					if(len == 1) {
						try {
							SourceMethod sourceMethod = (SourceMethod) foundMethods[0];
							parameterNames = ((SourceMethodElementInfo) sourceMethod.getElementInfo()).getArgumentNames();
						} catch (JavaModelException e) {
							// method doesn't exist: ignore
						}
					}
				}
			}
		}
		return parameterNames;
	}

	private void findMethods(
		char[] selector,
		TypeBinding[] typeArgTypes,
		TypeBinding[] argTypes,
		ReferenceBinding receiverType,
		Scope scope,
		ObjectVector methodsFound,
		boolean onlyStaticMethods,
		boolean exactMatch,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean implicitCall,
		boolean superCall,
		boolean canBePrefixed,
		Binding[] missingElements,
		int[] missingElementsStarts,
		int[] missingElementsEnds,
		boolean missingElementsHaveProblems,
		char[] castedReceiver,
		int receiverStart,
		int receiverEnd) {

		boolean notInJavadoc = this.assistNodeInJavadoc == 0;
		if (selector == null && notInJavadoc) {
			return;
		}

		if (this.assistNodeIsInsideCase)
			return;		// no methods should be proposed inside case expression

		ReferenceBinding currentType = receiverType;
		if (notInJavadoc) {
			if (receiverType.isInterface()) {
				findInterfacesMethods(
					selector,
					typeArgTypes,
					argTypes,
					receiverType,
					new ReferenceBinding[]{currentType},
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems,
					castedReceiver,
					receiverStart,
					receiverEnd);

				currentType = scope.getJavaLangObject();
			}
		}
		boolean hasPotentialDefaultAbstractMethods = true;
		boolean java8Plus = this.compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8;
		while (currentType != null) {

			MethodBinding[] methods = currentType.availableMethods();
			if (methods != null) {
				findLocalMethods(
					selector,
					typeArgTypes,
					argTypes,
					methods,
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					receiverType,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems,
					castedReceiver,
					receiverStart,
					receiverEnd);
			}

			/* Searching of superinterfaces for candidate proposal methods can be skipped if current type is concrete, but only for source levels below 1.8.
			   For 1.8 even a concrete type's superinterfaces should be searched as they could have default methods which are not implemented by the concrete
			   type.
			*/
			if (hasPotentialDefaultAbstractMethods &&
					(java8Plus || (currentType.isAbstract() || currentType.isTypeVariable() || currentType.isIntersectionType() || currentType.isEnum()))) {

				ReferenceBinding[] superInterfaces = currentType.superInterfaces();
				if (superInterfaces != null && currentType.isIntersectionType()) {
					for (int i = 0; i < superInterfaces.length; i++) {
						superInterfaces[i] = (ReferenceBinding)superInterfaces[i].capture(invocationScope, invocationSite.sourceStart(), invocationSite.sourceEnd());
					}
				}

				findInterfacesMethods(
					selector,
					typeArgTypes,
					argTypes,
					receiverType,
					superInterfaces,
					scope,
					methodsFound,
					onlyStaticMethods,
					exactMatch,
					invocationSite,
					invocationScope,
					implicitCall,
					superCall,
					canBePrefixed,
					missingElements,
					missingElementsStarts,
					missingElementsEnds,
					missingElementsHaveProblems,
					castedReceiver,
					receiverStart,
					receiverEnd);
			} else {
				if (!java8Plus)
					hasPotentialDefaultAbstractMethods = false;
			}
			currentType = currentType.superclass();
		}
	}

	private void findNestedTypes(
		char[] typeName,
		SourceTypeBinding currentType,
		Scope scope,
		boolean proposeAllMemberTypes,
		ObjectVector typesFound) {

		if (typeName == null)
			return;

		int typeLength = typeName.length;

		SourceTypeBinding nextTypeToIgnore = null;
		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.subscopeCount; i < length; i++) {

						if (blockScope.subscopes[i] instanceof ClassScope) {
							SourceTypeBinding localType =
								((ClassScope) blockScope.subscopes[i]).referenceContext.binding;

							if (!localType.isAnonymousType()) {
								if (isForbidden(localType))
									continue next;

								if (typeLength > localType.sourceName.length)
									continue next;
								if (isFailedMatch(typeName, localType.sourceName))
									continue next;

								for (int j = typesFound.size; --j >= 0;) {
									ReferenceBinding otherType = (ReferenceBinding) typesFound.elementAt(j);

									if (TypeBinding.equalsEquals(localType, otherType))
										continue next;
								}

								if (this.assistNodeIsExtendedType && localType.isFinal()) continue next;
								if (this.assistNodeIsInterfaceExcludingAnnotation && localType.isAnnotationType()) continue next;
								if(this.assistNodeIsClass) {
									if(!localType.isClass()) continue next;
								} else if(this.assistNodeIsInterface) {
									if(!localType.isInterface() && !localType.isAnnotationType()) continue next;
								} else if (this.assistNodeIsAnnotation) {
									if(!localType.isAnnotationType()) continue next;
								}

								int relevance = computeBaseRelevance();
								relevance += computeRelevanceForResolution();
								relevance += computeRelevanceForInterestingProposal(localType);
								relevance += computeRelevanceForCaseMatching(typeName, localType.sourceName);
								relevance += computeRelevanceForExpectingType(localType);
								relevance += computeRelevanceForException(localType.sourceName);
								relevance += computeRelevanceForClass();
								relevance += computeRelevanceForQualification(false);
								relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for nested type
								relevance += computeRelevanceForAnnotationTarget(localType);

								boolean allowingLongComputationProposals = isAllowingLongComputationProposals();
								if (!this.assistNodeIsConstructor || !allowingLongComputationProposals || hasArrayTypeAsExpectedSuperTypes()) {
									this.noProposal = false;
									if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
										createTypeProposal(
												localType,
												localType.sourceName,
												IAccessRule.K_ACCESSIBLE,
												localType.sourceName,
												relevance,
												null,
												null,
												null,
												false);
									}
								}

								if (this.assistNodeIsConstructor && allowingLongComputationProposals) {
									findConstructorsOrAnonymousTypes(
											localType,
											blockScope,
											FakeInvocationSite,
											false,
											relevance);
								}
							}
						}
					}
					break;

				case Scope.CLASS_SCOPE :
					SourceTypeBinding enclosingSourceType = scope.enclosingSourceType();
					findMemberTypes(
							typeName,
							enclosingSourceType,
							scope,
							currentType,
							false,
							false,
							false,
							false,
							proposeAllMemberTypes,
							nextTypeToIgnore,
							typesFound,
							null,
							null,
							null,
							false);
					nextTypeToIgnore = enclosingSourceType;
					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 proposeModuleName(CompilationUnitDeclaration parsedUnit) {
		String projectName = this.javaProject.getElementName();
		char[] moduleName = projectName.toCharArray();
		if (moduleName.length > 0) {// do not propose invalid names
			if (!Character.isJavaIdentifierStart(moduleName[0])) return;
			for (char c : moduleName) {
				if (!Character.isJavaIdentifierPart(c) && c != '.') return;
			}
		}
		this.completionToken = CharOperation.concatWith(this.moduleDeclaration.tokens, '.');
		setSourceRange(this.moduleDeclaration.sourceStart, this.moduleDeclaration.bodyStart);
		if (this.completionToken.length > 0 && !CharOperation.prefixEquals(this.completionToken, moduleName)) return;

		InternalCompletionProposal proposal =  createProposal(CompletionProposal.MODULE_DECLARATION, this.actualCompletionPosition);
		proposal.setName(moduleName);
		proposal.setDeclarationSignature(moduleName);
		proposal.setCompletion(moduleName);
		proposal.setReplaceRange((this.startPosition < 0) ? 0 : this.startPosition - this.offset, this.endPosition - this.offset);
		proposal.setTokenRange((this.tokenStart < 0) ? 0 : this.tokenStart - this.offset, this.tokenEnd - this.offset);
		proposal.setRelevance(R_MODULE_DECLARATION);
		this.requestor.accept(proposal);
		if(DEBUG) {
			this.printDebug(proposal);
		}
	}

	private HashSet<String> getAllJarModuleNames(IJavaProject javaProject2) {
		HashSet<String> modules = new HashSet<>();
		try {
			for (IPackageFragmentRoot root : javaProject2.getAllPackageFragmentRoots()) {
				if (root instanceof JarPackageFragmentRoot) {
					IModuleDescription desc = root.getModuleDescription();
					desc = desc == null ? ((JarPackageFragmentRoot) root).getAutomaticModuleDescription() : desc;
					String name = desc != null ? desc.getElementName() : null;
					if (name != null && name.length() > 0)
						modules.add(name);
				}
			}
		} catch (JavaModelException e) {
			// do nothing
		}
		return modules;
	}
	private void findTargettedModules(char[] prefix, HashSet<String> skipSet) {
		HashSet<String> probableModules = new HashSet<>();
		ModuleSourcePathManager mManager = JavaModelManager.getModulePathManager();
		JavaElementRequestor javaElementRequestor = new JavaElementRequestor();
		try {
			mManager.seekModule(this.completionToken, true, javaElementRequestor);
			IModuleDescription[] modules = javaElementRequestor.getModules();
			for (IModuleDescription module : modules) {
				String name = module.getElementName();
				if (name == null || name.equals("")) //$NON-NLS-1$
					continue;
				probableModules.add(name);
			}
		} catch (JavaModelException e) {
			// TODO ignore for now
		}
		probableModules.addAll(getAllJarModuleNames(this.javaProject));
		if (prefix != CharOperation.ALL_PREFIX && prefix != null && prefix.length > 0) {
			probableModules.removeIf(e -> isFailedMatch(prefix, e.toCharArray()));
		}
		for (String s : probableModules) {
			if (!skipSet.contains(s))
				this.acceptModule(s.toCharArray());
		}
	}
	private void findTargettedModules(CompletionOnModuleReference moduleReference, HashSet<String> skipSet) {
		setCompletionToken(moduleReference.tokens, moduleReference.sourceStart, moduleReference.sourceEnd, moduleReference.sourcePositions);
		findTargettedModules(CharOperation.toLowerCase(this.completionToken), skipSet);
	}
	private void findTargettedModules(CompletionOnJavadocQualifiedTypeReference typeReference, HashSet<String> skipSet) {
		setCompletionToken(typeReference.tokens, typeReference.sourceStart, typeReference.sourceEnd, typeReference.sourcePositions);
		findTargettedModules(CharOperation.toLowerCase(this.completionToken), skipSet);
	}

	private void setCompletionToken(char[][] tokens, int sourceStart, int sourceEnd, long[] sourcePositions, boolean without) {
		this.completionToken = without ? CharOperation.concatWith(tokens, '.') : CharOperation.concatWithAll(tokens, '.');
		if (this.completionToken.length == 0)
			this.completionToken = CharOperation.ALL_PREFIX;
		setSourceRange(sourceStart, sourceEnd);
		long completionPosition = sourcePositions[sourcePositions.length - 1];
		setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
	}
	private void setCompletionToken(char[][] tokens, int sourceStart, int sourceEnd, long[] sourcePositions) {
		setCompletionToken(tokens, sourceStart, sourceEnd, sourcePositions, tokens.length > 0 && tokens[tokens.length - 1].length > 0);
	}
	private void findModules(CompletionOnModuleReference moduleReference, boolean targetted) {
		setCompletionToken(moduleReference.tokens, moduleReference.sourceStart, moduleReference.sourceEnd, moduleReference.sourcePositions);
		findTargettedModules(moduleReference, new HashSet<>()); // empty skipSet passed
		this.nameEnvironment.findModules(CharOperation.toLowerCase(this.completionToken), this, targetted ? this.javaProject : null);
	}
	private void findModules(CompletionOnJavadocQualifiedTypeReference typeReference, boolean targetted) {
		if (JavaCore.compareJavaVersions(this.sourceLevel, JavaCore.VERSION_15) >= 0 ) {
			boolean isIgnoredModuleRef = false;
			try {
				if (isIgnored(CompletionProposal.MODULE_REF)) {
					this.requestor.setIgnored(CompletionProposal.MODULE_REF, false);
					isIgnoredModuleRef = true;
				}
				setCompletionToken(typeReference.tokens, typeReference.sourceStart, typeReference.sourceEnd, typeReference.sourcePositions);
				findTargettedModules(typeReference, new HashSet<>()); // empty skipSet passed
				this.nameEnvironment.findModules(CharOperation.toLowerCase(this.completionToken), this, targetted ? this.javaProject : null);
			} finally {
				this.requestor.setIgnored(CompletionProposal.MODULE_REF, isIgnoredModuleRef);
			}
		}
	}
	private void findModules(CompletionOnJavadocSingleTypeReference typeReference, boolean targetted) {
		if (JavaCore.compareJavaVersions(this.sourceLevel, JavaCore.VERSION_15) >= 0 ) {
			boolean isIgnoredModuleRef = false;
			try {
				if (isIgnored(CompletionProposal.MODULE_REF)) {
					this.requestor.setIgnored(CompletionProposal.MODULE_REF, false);
					isIgnoredModuleRef = true;
				}
				this.nameEnvironment.findModules(CharOperation.toLowerCase(typeReference.token), this, targetted ? this.javaProject : null);
			} finally {
				this.requestor.setIgnored(CompletionProposal.MODULE_REF, isIgnoredModuleRef);
			}
		}
	}
	private void findPackages(CompletionOnPackageVisibilityReference reference) {
		setCompletionToken(reference.tokens, reference.sourceStart, reference.sourceEnd, reference.sourcePositions, false);
		findPackagesInCurrentModule();
	}

	private void findPackagesInCurrentModule() {
		try {
			IPackageFragmentRoot[] moduleRoots = SearchableEnvironment.getOwnedPackageFragmentRoots(this.javaProject);
			this.nameEnvironment.findPackages(CharOperation.toLowerCase(this.completionToken), this, moduleRoots, false);
		} catch (JavaModelException e) {
			// silent
		}
	}
	private void findPackages(CompletionOnPackageReference packageStatement) {
		this.completionToken = CharOperation.concatWithAll(packageStatement.tokens, '.');
		if (this.completionToken.length == 0)
			return;
		setSourceRange(packageStatement.sourceStart, packageStatement.sourceEnd);
		long completionPosition = packageStatement.sourcePositions[packageStatement.sourcePositions.length - 1];
		setTokenRange((int) (completionPosition >>> 32), (int) completionPosition);
		try {
			this.nameEnvironment.findPackages(CharOperation.toLowerCase(this.completionToken), this, this.javaProject.getAllPackageFragmentRoots(), true);
		} catch (JavaModelException e) {
			// silent
		}
	}

	private void findParameterizedType(TypeReference ref, Scope scope) {
		ReferenceBinding refBinding = (ReferenceBinding) ref.resolvedType;
		if(refBinding != null) {
			if (this.options.checkDeprecation &&
					refBinding.isViewedAsDeprecated() &&
					!scope.isDefinedInSameUnit(refBinding))
				return;

			int accessibility = IAccessRule.K_ACCESSIBLE;
			if(refBinding.hasRestrictedAccess()) {
				AccessRestriction accessRestriction = this.lookupEnvironment.getAccessRestriction(refBinding);
				if(accessRestriction != null) {
					switch (accessRestriction.getProblemId()) {
						case IProblem.ForbiddenReference:
							if (this.options.checkForbiddenReference) {
								return;
							}
							accessibility = IAccessRule.K_NON_ACCESSIBLE;
							break;
						case IProblem.DiscouragedReference:
							if (this.options.checkDiscouragedReference) {
								return;
							}
							accessibility = IAccessRule.K_DISCOURAGED;
							break;
					}
				}
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForCaseMatching(refBinding.sourceName, refBinding.sourceName);
			relevance += computeRelevanceForExpectingType(refBinding);
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(accessibility); // no access restriction for type in the current unit

			if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
				createTypeProposal(
						refBinding,
						refBinding.qualifiedSourceName(),
						IAccessRule.K_ACCESSIBLE,
						CharOperation.NO_CHAR,
						relevance,
						null,
						null,
						null,
						false);
			}
		}
	}

	private void findSubMemberTypes(
		char[] typeName,
		ReferenceBinding receiverType,
		Scope scope,
		SourceTypeBinding typeInvocation,
		boolean staticOnly,
		boolean staticFieldsAndMethodOnly,
		boolean fromStaticImport,
		ObjectVector typesFound) {

		ReferenceBinding currentType = receiverType;
		if (typeName == null)
			return;

		if (this.assistNodeIsSuperType && !this.insideQualifiedReference && isForbidden(currentType)) return; // we're trying to find a supertype

		findMemberTypes(
				typeName,
				currentType.memberTypes(),
				typesFound,
				receiverType,
				typeInvocation,
				staticOnly,
				staticFieldsAndMethodOnly,
				fromStaticImport,
				true,
				scope,
				null,
				null,
				null,
				false);

		ReferenceBinding[] memberTypes = receiverType.memberTypes();
		next : for (int i = 0; i < memberTypes.length; i++) {
			if (this.options.checkVisibility) {
				if (typeInvocation != null && !memberTypes[i].canBeSeenBy(receiverType, typeInvocation)) {
					continue next;
				} else if(typeInvocation == null && !memberTypes[i].canBeSeenBy(this.unitScope.fPackage)) {
					continue next;
				}
			}
			findSubMemberTypes(
				typeName,
				memberTypes[i],
				scope,
				typeInvocation,
				staticOnly,
				staticFieldsAndMethodOnly,
				fromStaticImport,
				typesFound);
		}
	}

	private void findTrueOrFalseKeywords(char[][] choices) {
		if(choices == null || choices.length == 0) return;

		if(this.expectedTypesPtr != 0 || TypeBinding.notEquals(this.expectedTypes[0], TypeBinding.BOOLEAN)) return;

		for (int i = 0; i < choices.length; i++) {
			if (CharOperation.equals(choices[i], Keywords.TRUE) ||
					CharOperation.equals(choices[i], Keywords.FALSE)
			){
				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal();
				relevance += computeRelevanceForCaseMatching(CharOperation.NO_CHAR, choices[i]);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for keywors
				relevance += computeRelevanceForExpectingType(TypeBinding.BOOLEAN);
				relevance += computeRelevanceForQualification(false);
				relevance += R_TRUE_OR_FALSE;

				this.noProposal = false;
				if(!this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
					InternalCompletionProposal proposal =  createProposal(CompletionProposal.KEYWORD, this.actualCompletionPosition);
					proposal.setName(choices[i]);
					proposal.setCompletion(choices[i]);
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					this.requestor.accept(proposal);
					if(DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}

	private void findTypeParameters(char[] token, Scope scope) {
		if (this.compilerOptions.sourceLevel < ClassFileConstants.JDK1_5) return;

		TypeParameter[] typeParameters = null;
		while (scope != null) { // done when a COMPILATION_UNIT_SCOPE is found
			typeParameters = null;
			switch (scope.kind) {
				case Scope.METHOD_SCOPE :
					MethodScope methodScope = (MethodScope) scope;
					if(methodScope.referenceContext instanceof MethodDeclaration) {
						MethodDeclaration methodDeclaration = (MethodDeclaration) methodScope.referenceContext;
						typeParameters = methodDeclaration.typeParameters;
					} else if(methodScope.referenceContext instanceof ConstructorDeclaration) {
						ConstructorDeclaration methodDeclaration = (ConstructorDeclaration) methodScope.referenceContext;
						typeParameters = methodDeclaration.typeParameters;
					}
					break;
				case Scope.CLASS_SCOPE :
					ClassScope classScope = (ClassScope) scope;
					typeParameters = classScope.referenceContext.typeParameters;
					break;
				case Scope.COMPILATION_UNIT_SCOPE :
					return;
			}
			if(typeParameters != null) {
				for (int i = 0; i < typeParameters.length; i++) {
					int typeLength = token.length;
					TypeParameter typeParameter = typeParameters[i];

					if(typeParameter.binding == null) continue;

					if (typeLength > typeParameter.name.length) continue;

					if (isFailedMatch(token, typeParameter.name)) continue;

					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForResolution();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(token, typeParameter.name);
					relevance += computeRelevanceForExpectingType(typeParameter.type == null ? null :typeParameter.type.resolvedType);
					relevance += computeRelevanceForQualification(false);
					relevance += computeRelevanceForException(typeParameter.name);
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction fot type parameter

					this.noProposal = false;
					if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
						createTypeParameterProposal(typeParameter, relevance);
					}
				}
			}
			scope = scope.parent;
		}
	}

	private void findTypesAndPackages(char[] token, Scope scope, boolean proposeBaseTypes, boolean proposeVoidType, ObjectVector typesFound) {

		if (token == null)
			return;

		boolean allowingLongComputationProposals = isAllowingLongComputationProposals();

		boolean proposeType =
			!this.requestor.isIgnored(CompletionProposal.TYPE_REF) ||
			((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_TYPE_REF));

		boolean proposeAllMemberTypes = !this.assistNodeIsConstructor;

		boolean proposeConstructor =
			allowingLongComputationProposals &&
			this.assistNodeIsConstructor &&
			(!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF) ||
					!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF));


		boolean isEmptyPrefix = token.length == 0;

		checkCancel();
		if (proposeConstructor) {
			findTypesFromExpectedTypes(token, scope, typesFound, proposeType, true);
		} else if(isEmptyPrefix && !this.assistNodeIsAnnotation) {
			findTypesFromExpectedTypes(token, scope, typesFound, proposeType, false);
		}

		if ((proposeType || proposeConstructor) && scope.enclosingSourceType() != null) {

			checkCancel();

			findNestedTypes(token, scope.enclosingSourceType(), scope, proposeAllMemberTypes, typesFound);
			if(!this.assistNodeIsInterface &&
					!this.assistNodeIsConstructor &&
					!this.assistNodeIsAnnotation &&
					this.assistNodeInJavadoc == 0) {

				checkCancel();

				// don't propose type parameters if the completion is a constructor ('new |')
				findTypeParameters(token, scope);
			}
		}

		if ((proposeType || proposeConstructor) && this.unitScope != null) {

			ReferenceBinding outerInvocationType = scope.enclosingSourceType();
			if(outerInvocationType != null) {
				ReferenceBinding temp = outerInvocationType.enclosingType();
				while(temp != null) {
					outerInvocationType = temp;
					temp = temp.enclosingType();
				}
			}

			int typeLength = token.length;
			SourceTypeBinding[] types = this.unitScope.topLevelTypes;

			next : for (int i = 0, length = types.length; i < length; i++) {

				checkCancel();

				SourceTypeBinding sourceType = types[i];

				if(isForbidden(sourceType)) continue next;

				if(proposeAllMemberTypes &&
					TypeBinding.notEquals(sourceType, outerInvocationType)) {
					findSubMemberTypes(
							token,
							sourceType,
							scope,
							scope.enclosingSourceType(),
							false,
							false,
							false,
							typesFound);
				}

				if (sourceType.sourceName == CompletionParser.FAKE_TYPE_NAME) continue next;
				if (sourceType.sourceName == TypeConstants.PACKAGE_INFO_NAME) continue next;

				if (typeLength > sourceType.sourceName.length) continue next;

				if (isFailedMatch(token, sourceType.sourceName)) continue next;

				if (this.assistNodeIsAnnotation && !hasPossibleAnnotationTarget(sourceType, scope)) {
					continue next;
				}

				for (int j = typesFound.size; --j >= 0;) {
					ReferenceBinding otherType = (ReferenceBinding) typesFound.elementAt(j);

					if (TypeBinding.equalsEquals(sourceType, otherType)) continue next;
				}

				typesFound.add(sourceType);

				if (this.assistNodeIsExtendedType && sourceType.isFinal()) continue next;
				if (this.assistNodeIsExtendedType && sourceType.isRecord()) continue next;
				if (this.assistNodeIsInterfaceExcludingAnnotation && sourceType.isAnnotationType()) continue next;
				if(this.assistNodeIsClass) {
					if(!sourceType.isClass()) continue next;
				} else if(this.assistNodeIsInterface) {
					if(!sourceType.isInterface() && !sourceType.isAnnotationType()) continue next;
				} else if (this.assistNodeIsAnnotation) {
					if(!sourceType.isAnnotationType()) continue next;
				} else if (this.assistNodeIsException) {
					 if (!sourceType.isClass()) continue next;
					 if (isEmptyPrefix) {
						 if (sourceType.findSuperTypeOriginatingFrom(TypeIds.T_JavaLangThrowable, true) == null) {
							 continue next;
					     }
					  }
				}

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal(sourceType);
				relevance += computeRelevanceForCaseMatching(token, sourceType.sourceName);
				relevance += computeRelevanceForExpectingType(sourceType);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for type in the current unit

				if (sourceType.isAnnotationType()) {
					relevance += computeRelevanceForAnnotation();
					relevance += computeRelevanceForAnnotationTarget(sourceType);
				} else if (sourceType.isInterface()) {
					relevance += computeRelevanceForInterface();
				} else if(sourceType.isClass()){
					relevance += computeRelevanceForClass();
					relevance += computeRelevanceForException(sourceType.sourceName);
				}


				this.noProposal = false;
				if(proposeType &&
						(!this.assistNodeIsConstructor ||
								!allowingLongComputationProposals ||
								hasStaticMemberTypes(sourceType, null, this.unitScope) ||
								hasMemberTypesInEnclosingScope(sourceType, scope)) ||
								hasArrayTypeAsExpectedSuperTypes()) {
					char[] typeName = sourceType.sourceName();
					createTypeProposal(
								sourceType,
								typeName,
								IAccessRule.K_ACCESSIBLE,
								typeName,
								relevance,
								null,
								null,
								null,
								false);
				}

				if (proposeConstructor) {
					findConstructorsOrAnonymousTypes(
							sourceType,
							scope,
							FakeInvocationSite,
							false,
							relevance);
				}
			}
		}

		if (proposeConstructor && !isEmptyPrefix) {

			checkCancel();

			findTypesFromImports(token, scope, proposeType, typesFound);
		} else if(proposeType) {

			checkCancel();

			findTypesFromStaticImports(token, scope, proposeAllMemberTypes, typesFound);
		}

		if (isEmptyPrefix && !this.assistNodeIsAnnotation) {
			if (proposeConstructor) {
				findConstructorsFromSubTypes(scope, typesFound);
			}
		} else {
			if(!isEmptyPrefix && !this.requestor.isIgnored(CompletionProposal.KEYWORD)) {
				if (this.assistNodeInJavadoc == 0 || (this.assistNodeInJavadoc & CompletionOnJavadoc.BASE_TYPES) != 0) {
					if (proposeBaseTypes) {
						if (proposeVoidType) {
							findKeywords(token, BASE_TYPE_NAMES, false, false);
						} else {
							findKeywords(token, BASE_TYPE_NAMES_WITHOUT_VOID, false, false);
						}
					}
				}
			}

			if (proposeConstructor) {
				int l = typesFound.size();
				for (int i = 0; i < l; i++) {
					ReferenceBinding typeFound = (ReferenceBinding) typesFound.elementAt(i);
					char[] fullyQualifiedTypeName =
						CharOperation.concat(
								typeFound.qualifiedPackageName(),
								typeFound.qualifiedSourceName(),
								'.');
					this.knownTypes.put(fullyQualifiedTypeName, KNOWN_TYPE_WITH_KNOWN_CONSTRUCTORS);
				}

				checkCancel();

				this.foundConstructorsCount = 0;
				this.nameEnvironment.findConstructorDeclarations(
						token,
						getTypesMatchRule(),
						false,
						this,
						this.monitor);
				acceptConstructors(scope);
			} else if (proposeType) {
				int l = typesFound.size();
				for (int i = 0; i < l; i++) {
					ReferenceBinding typeFound = (ReferenceBinding) typesFound.elementAt(i);
					char[] fullyQualifiedTypeName =
						CharOperation.concat(
								typeFound.qualifiedPackageName(),
								typeFound.qualifiedSourceName(),
								'.');
					this.knownTypes.put(fullyQualifiedTypeName, KNOWN_TYPE_WITH_KNOWN_CONSTRUCTORS);
				}
				int searchFor = IJavaSearchConstants.TYPE;
				if(this.assistNodeIsClass || this.assistNodeIsException) {
					searchFor = IJavaSearchConstants.CLASS;
				} else if (this.assistNodeIsInterfaceExcludingAnnotation) {
					searchFor = IJavaSearchConstants.INTERFACE;
				} else if(this.assistNodeIsInterface) {
					searchFor = IJavaSearchConstants.INTERFACE_AND_ANNOTATION;
				} else if(this.assistNodeIsEnum) {
					searchFor = IJavaSearchConstants.ENUM;
				} else if(this.assistNodeIsAnnotation) {
					searchFor = IJavaSearchConstants.ANNOTATION_TYPE;
				}

				checkCancel();

				this.foundTypesCount = 0;
				this.nameEnvironment.findTypes(
						token,
						proposeAllMemberTypes,
						getTypesMatchRule(),
						searchFor,
						false,
						this,
						this.monitor);
				acceptTypes(scope);
			}
			if(!isEmptyPrefix && !this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {

				checkCancel();

				this.nameEnvironment.findPackages(token, this);
			}
		}
	}

	/**
	 * Find all the constructors of the expected types' sub types.
	 */
	private void findConstructorsFromSubTypes(Scope scope, ObjectVector typesFound) {
		SearchPattern pattern = null;
		for (TypeBinding typeBinding : this.expectedTypes) {
			if (typeBinding == null || !typeBinding.isInterface()) {
				continue;
			}
			if (pattern == null) {
				pattern = SearchPattern.createPattern(String.valueOf(typeBinding.qualifiedPackageName()) +
						"." + String.valueOf(typeBinding.qualifiedSourceName()), //$NON-NLS-1$
						IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS,
						SearchPattern.R_EXACT_MATCH);
			} else {
				pattern = SearchPattern.createOrPattern(pattern, SearchPattern.createPattern(String.valueOf(
						typeBinding.qualifiedPackageName()) + "." + String.valueOf(typeBinding.qualifiedSourceName()), //$NON-NLS-1$
						IJavaSearchConstants.CLASS, IJavaSearchConstants.IMPLEMENTORS,
						SearchPattern.R_EXACT_MATCH));
			}
		}
		if (pattern == null) {
			return;
		}

		List<IType> types = new ArrayList<>();
		IJavaSearchScope searchScope = SearchEngine.createJavaSearchScope(new IJavaElement[] {this.javaProject});
		SearchRequestor searchRequstor = new SearchRequestor() {
			@Override
			public void acceptSearchMatch(SearchMatch match) {
				Object element = match.getElement();
				if (!(element instanceof IType)) {
					return;
				}
				types.add((IType) element);
			}
		};
		try {
			new SearchEngine().search(pattern,
					new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
					searchScope,
					searchRequstor,
					null
			);
		} catch (CoreException e) {
			if(DEBUG) {
				trace("Exception caught by CompletionEngine:", e); //$NON-NLS-1$
			}
		}

		checkCancel();

		for (IType type : types) {
			ReferenceBinding typeBinding = this.lookupEnvironment.getType(CharOperation.splitOn('.', type.getFullyQualifiedName().toCharArray()));
			if (typeBinding == null) {
				continue;
			}

			int accessibility = IAccessRule.K_ACCESSIBLE;
			if(typeBinding.hasRestrictedAccess()) {
				AccessRestriction accessRestriction = this.lookupEnvironment.getAccessRestriction(typeBinding);
				if(accessRestriction != null) {
					switch (accessRestriction.getProblemId()) {
						case IProblem.ForbiddenReference:
							if (this.options.checkForbiddenReference) {
								continue;
							}
							accessibility = IAccessRule.K_NON_ACCESSIBLE;
							break;
						case IProblem.DiscouragedReference:
							if (this.options.checkDiscouragedReference) {
								continue;
							}
							accessibility = IAccessRule.K_DISCOURAGED;
							break;
					}
				}
			}

			if (typesFound.contains(typeBinding)) continue;

			typesFound.add(typeBinding);

			if (this.assistNodeIsExtendedType && typeBinding.isFinal()) continue;
			if (this.assistNodeIsInterfaceExcludingAnnotation && typeBinding.isAnnotationType()) continue;
			if(this.assistNodeIsClass) {
				if(!typeBinding.isClass()) continue;
			} else if(this.assistNodeIsInterface) {
				if(!typeBinding.isInterface() && !typeBinding.isAnnotationType()) continue;
			} else if (this.assistNodeIsAnnotation) {
				if(!typeBinding.isAnnotationType()) continue;
			}

			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal(typeBinding);
			relevance += R_EXACT_EXPECTED_TYPE;	// all found types are sub-types
			relevance += computeRelevanceForQualification(false);
			relevance += computeRelevanceForRestrictions(accessibility);

			if (typeBinding.isAnnotationType()) {
				relevance += computeRelevanceForAnnotation();
				relevance += computeRelevanceForAnnotationTarget(typeBinding);
			} else if (typeBinding.isInterface()) {
				relevance += computeRelevanceForInterface();
			} else if(typeBinding.isClass()){
				relevance += computeRelevanceForClass();
				relevance += computeRelevanceForException(typeBinding.sourceName);
			}

			if (!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
				boolean isQualified = false;
				if (!this.insideQualifiedReference && !typeBinding.isMemberType()) {
					char[] packageName = typeBinding.qualifiedPackageName();
					char[] typeName = typeBinding.sourceName();
					if (mustQualifyType(packageName, typeName, null, typeBinding.modifiers)) {
						isQualified = true;
					}
				}
				findConstructors(
					typeBinding,
					null,
					scope,
					FakeInvocationSite,
					false,
					null,
					null,
					null,
					false,
					false,
					isQualified,
					relevance
				);
			}
		}
	}

	private int getTypesMatchRule() {
		int matchRule = SearchPattern.R_PREFIX_MATCH;
		if (this.options.camelCaseMatch)
			matchRule |= SearchPattern.R_CAMELCASE_MATCH;
		if (this.options.substringMatch)
			matchRule |= SearchPattern.R_SUBSTRING_MATCH;
		if (this.options.subwordMatch)
			matchRule |= SearchPattern.R_SUBWORD_MATCH;
		return matchRule;
	}

	private void findTypesAndSubpackages(
		char[] token,
		PackageBinding packageBinding,
		Scope scope) {

		boolean allowingLongComputationProposals = isAllowingLongComputationProposals();

		boolean proposeType =
			!this.requestor.isIgnored(CompletionProposal.TYPE_REF) ||
			((this.assistNodeInJavadoc & CompletionOnJavadoc.TEXT) != 0 && !this.requestor.isIgnored(CompletionProposal.JAVADOC_TYPE_REF));

		boolean proposeConstructor =
			allowingLongComputationProposals &&
			this.assistNodeIsConstructor &&
			(!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF) ||
					!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF));

		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] = '.';
		}

		this.qualifiedCompletionToken = qualifiedName;

		if ((proposeType || proposeConstructor) && this.unitScope != null) {
			int typeLength = qualifiedName.length;
			SourceTypeBinding[] types = this.unitScope.topLevelTypes;

			for (int i = 0, length = types.length; i < length; i++) {

				checkCancel();

				SourceTypeBinding sourceType = types[i];

				if (isForbidden(sourceType)) continue;
				if (this.assistNodeIsClass && sourceType.isInterface()) continue;
				if (this.assistNodeIsInterface && sourceType.isClass()) continue;

				char[] qualifiedSourceTypeName = CharOperation.concatWith(sourceType.compoundName, '.');

				if (sourceType.sourceName == CompletionParser.FAKE_TYPE_NAME) continue;
				if (sourceType.sourceName == TypeConstants.PACKAGE_INFO_NAME) continue;
				if (typeLength > qualifiedSourceTypeName.length) continue;
				if (!(packageBinding == sourceType.getPackage())) continue;

				if (!CharOperation.prefixEquals(qualifiedName, qualifiedSourceTypeName, false)
						&& !(this.options.camelCaseMatch && CharOperation.camelCaseMatch(token, sourceType.sourceName)))	continue;

				if (this.options.checkDeprecation &&
						sourceType.isViewedAsDeprecated() &&
						!scope.isDefinedInSameUnit(sourceType))
					continue;

			    if (this.assistNodeIsExtendedType && sourceType.isFinal()) continue;
			    if (this.assistNodeIsInterfaceExcludingAnnotation && sourceType.isAnnotationType()) continue;
				int accessibility = IAccessRule.K_ACCESSIBLE;
				if(sourceType.hasRestrictedAccess()) {
					AccessRestriction accessRestriction = this.lookupEnvironment.getAccessRestriction(sourceType);
					if(accessRestriction != null) {
						switch (accessRestriction.getProblemId()) {
							case IProblem.ForbiddenReference:
								if (this.options.checkForbiddenReference) {
									continue;
								}
								accessibility = IAccessRule.K_NON_ACCESSIBLE;
								break;
							case IProblem.DiscouragedReference:
								if (this.options.checkDiscouragedReference) {
									continue;
								}
								accessibility = IAccessRule.K_DISCOURAGED;
								break;
						}
					}
				}

				this.knownTypes.put(CharOperation.concat(sourceType.qualifiedPackageName(), sourceType.sourceName(), '.'), KNOWN_TYPE_WITH_KNOWN_CONSTRUCTORS);

				int relevance = computeBaseRelevance();
				relevance += computeRelevanceForResolution();
				relevance += computeRelevanceForInterestingProposal(sourceType);
				relevance += computeRelevanceForCaseMatching(qualifiedName, qualifiedSourceTypeName);
				relevance += computeRelevanceForExpectingType(sourceType);
				relevance += computeRelevanceForQualification(false);
				relevance += computeRelevanceForRestrictions(accessibility);

				if (sourceType.isAnnotationType()) {
					relevance += computeRelevanceForAnnotation();
				} else if (sourceType.isInterface()) {
					relevance += computeRelevanceForInterface();
				} else if (sourceType.isClass()) {
					relevance += computeRelevanceForClass();
					relevance += computeRelevanceForException(sourceType.sourceName);
				}

				this.noProposal = false;
				if(proposeType &&
						(!this.assistNodeIsConstructor ||
								!allowingLongComputationProposals ||
								hasStaticMemberTypes(sourceType, null, this.unitScope) ||
								hasMemberTypesInEnclosingScope(sourceType, scope)) ||
								hasArrayTypeAsExpectedSuperTypes()) {
					char[] typeName = sourceType.sourceName();
					createTypeProposal(
							sourceType,
							typeName,
							IAccessRule.K_ACCESSIBLE,
							typeName,
							relevance,
							null,
							null,
							null,
							false);
				}

				if (proposeConstructor) {
					findConstructorsOrAnonymousTypes(
							sourceType,
							scope,
							FakeInvocationSite,
							false,
							relevance);
				}
			}
		}

		if (proposeConstructor) {


			checkCancel();

			this.foundConstructorsCount = 0;
			this.nameEnvironment.findConstructorDeclarations(
					qualifiedName,
					getTypesMatchRule(),
					false,
					this,
					this.monitor);
			acceptConstructors(scope);
		} if(proposeType) {
			int searchFor = IJavaSearchConstants.TYPE;
			if(this.assistNodeIsClass) {
				searchFor = IJavaSearchConstants.CLASS;
			} else if (this.assistNodeIsInterfaceExcludingAnnotation) {
				searchFor = IJavaSearchConstants.INTERFACE;
			} else if(this.assistNodeIsInterface) {
				searchFor = IJavaSearchConstants.INTERFACE_AND_ANNOTATION;
			} else if(this.assistNodeIsEnum) {
				searchFor = IJavaSearchConstants.ENUM;
			} else if(this.assistNodeIsAnnotation) {
				searchFor = IJavaSearchConstants.ANNOTATION_TYPE;
			}

			checkCancel();

			this.foundTypesCount = 0;
			this.nameEnvironment.findTypes(
					qualifiedName,
					false,
					getTypesMatchRule(),
					searchFor,
					false,
					this,
					this.monitor);
			acceptTypes(scope);
		}

		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			this.nameEnvironment.findPackages(qualifiedName, this);
		}
	}

	private void findTypesFromExpectedTypes(char[] token, Scope scope, ObjectVector typesFound, boolean proposeType, boolean proposeConstructor) {
		if(this.expectedTypesPtr > -1) {
			boolean allowingLongComputationProposals = isAllowingLongComputationProposals();

			int typeLength = token == null ? 0 : token.length;

			next : for (int i = 0; i <= this.expectedTypesPtr; i++) {

				checkCancel();

				TypeBinding expectedType = this.expectedTypes[i];
				TypeBinding originalExpectedType = expectedType;
				int dimensions = 0;
				if (expectedType instanceof ArrayBinding) {
					ArrayBinding arrayBinding = (ArrayBinding) expectedType;
					expectedType = arrayBinding.leafComponentType();
					dimensions = arrayBinding.dimensions();
				}
				final boolean isArrayCompletion = dimensions > 0;

				if (expectedType instanceof ReferenceBinding) {
					ReferenceBinding refBinding = (ReferenceBinding) expectedType;

					if (typeLength > 0) {
						if (typeLength > refBinding.sourceName.length) continue next;

						if (isFailedMatch(token, refBinding.sourceName)) continue next;
					}


					if(refBinding.isTypeVariable() && this.assistNodeIsConstructor) {
						// don't propose type variable if the completion is a constructor ('new |')
						continue next;
					}
					if (this.options.checkDeprecation &&
							refBinding.isViewedAsDeprecated() &&
							!scope.isDefinedInSameUnit(refBinding))
						continue next;

					int accessibility = IAccessRule.K_ACCESSIBLE;
					if(refBinding.hasRestrictedAccess()) {
						AccessRestriction accessRestriction = this.lookupEnvironment.getAccessRestriction(refBinding);
						if(accessRestriction != null) {
							switch (accessRestriction.getProblemId()) {
								case IProblem.ForbiddenReference:
									if (this.options.checkForbiddenReference) {
										continue next;
									}
									accessibility = IAccessRule.K_NON_ACCESSIBLE;
									break;
								case IProblem.DiscouragedReference:
									if (this.options.checkDiscouragedReference) {
										continue next;
									}
									accessibility = IAccessRule.K_DISCOURAGED;
									break;
							}
						}
					}
					if(isForbidden(refBinding)) continue next;

					for (int j = 0; j < typesFound.size(); j++) {
						ReferenceBinding typeFound = (ReferenceBinding)typesFound.elementAt(j);
						if (TypeBinding.equalsEquals(typeFound, refBinding.erasure())) {
							continue next;
						}
					}

					boolean inSameUnit = this.unitScope.isDefinedInSameUnit(refBinding);

					// top level types of the current unit will be proposed by the caller unless expected type is an array.
					if(!inSameUnit || (inSameUnit && refBinding.isMemberType()) || (inSameUnit && isArrayCompletion)) {
						typesFound.add(refBinding);

						char[] packageName = refBinding.qualifiedPackageName();
						char[] typeName = refBinding.sourceName();
						char[] completionName = typeName;

						boolean isQualified = false;
						if (!this.insideQualifiedReference && !refBinding.isMemberType()) {
							if (mustQualifyType(packageName, typeName, null, refBinding.modifiers)) {
								if (packageName == null || packageName.length == 0)
									if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
										continue next; // ignore types from the default package from outside it
								completionName = CharOperation.concat(packageName, typeName, '.');
								isQualified = true;
							}
						}

						if (this.assistNodeIsExtendedType && refBinding.isFinal()) continue next;
						if (this.assistNodeIsInterfaceExcludingAnnotation && refBinding.isAnnotationType()) continue next;
						if(this.assistNodeIsClass) {
							if(!refBinding.isClass()) continue next;
						} else if(this.assistNodeIsInterface) {
							if(!refBinding.isInterface() && !refBinding.isAnnotationType()) continue next;
						} else if (this.assistNodeIsAnnotation) {
							if(!refBinding.isAnnotationType()) continue next;
						}

						int relevance = computeBaseRelevance();
						relevance += computeRelevanceForResolution();
						relevance += computeRelevanceForInterestingProposal(refBinding);
						relevance += computeRelevanceForCaseMatching(token, typeName);
						// if an array, then we need to use the original expected type.
						relevance += computeRelevanceForExpectingType(
							isArrayCompletion ? originalExpectedType : refBinding);
						relevance += computeRelevanceForQualification(isQualified);
						relevance += computeRelevanceForRestrictions(accessibility);

						if(refBinding.isClass()) {
							relevance += computeRelevanceForClass();
							relevance += computeRelevanceForException(typeName);
						} else if(refBinding.isEnum()) {
							relevance += computeRelevanceForEnum();
						} else if(refBinding.isInterface()) {
							relevance += computeRelevanceForInterface();
						}

						if (proposeType &&
								(!this.assistNodeIsConstructor || isArrayCompletion ||
										!allowingLongComputationProposals ||
										hasStaticMemberTypes(refBinding, scope.enclosingSourceType() ,this.unitScope)) ||
										hasArrayTypeAsExpectedSuperTypes()) {
							this.noProposal = false;
							if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
								InternalCompletionProposal proposal =  createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
								proposal.setDeclarationSignature(packageName);
								proposal.setSignature(getSignature(refBinding));
								proposal.setPackageName(packageName);
								proposal.setTypeName(typeName);
								proposal.setCompletion(completionName);
								proposal.setFlags(refBinding.modifiers);
								proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance);
								proposal.setAccessibility(accessibility);
								proposal.setArrayDimensions(dimensions);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}

						if (proposeConstructor && !isArrayCompletion) {
							findConstructorsOrAnonymousTypes(
									refBinding,
									scope,
									FakeInvocationSite,
									isQualified,
									relevance);
						}
					}
				} else if (expectedType instanceof BaseTypeBinding) {
					BaseTypeBinding baseType = (BaseTypeBinding) expectedType;
					// only go further if we are completing on an array.
					if (isArrayCompletion && proposeType && baseType.isPrimitiveType()) {
						this.noProposal = false;
						if (!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
							InternalCompletionProposal proposal = createProposal(CompletionProposal.TYPE_REF,
									this.actualCompletionPosition);

							char[] typeName = baseType.sourceName();
							char[] completionName = typeName;
							int relevance = computeBaseRelevance();
							relevance += computeRelevanceForResolution();
							relevance += computeRelevanceForInterestingProposal(baseType);
							relevance += computeRelevanceForCaseMatching(token, typeName);
							relevance += computeRelevanceForExpectingType(originalExpectedType); // use array type here
							relevance += computeRelevanceForQualification(true);
							relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

							proposal.setSignature(getSignature(baseType));
							proposal.setTypeName(typeName);
							proposal.setCompletion(completionName);
							proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
							proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
							proposal.setRelevance(relevance);
							proposal.setAccessibility(IAccessRule.K_ACCESSIBLE);
							proposal.setArrayDimensions(dimensions);
							this.requestor.accept(proposal);
							if (DEBUG) {
								this.printDebug(proposal);
							}
						}
					}

				}
			}
		}
	}

	private void findTypesFromImports(char[] token, Scope scope, boolean proposeType, ObjectVector typesFound) {
		ImportBinding[] importBindings = scope.compilationUnitScope().imports;
		next : for (int i = 0; i < importBindings.length; i++) {
			ImportBinding importBinding = importBindings[i];
			if(importBinding.isValidBinding()) {
				Binding binding = importBinding.resolvedImport;
				if(binding != null && binding.isValidBinding()) {
					if(importBinding.onDemand) {
						if (importBinding.isStatic()) {
							if((binding.kind() & Binding.TYPE) != 0) {
								this.findMemberTypes(
										token,
										(ReferenceBinding) binding,
										scope,
										scope.enclosingSourceType(),
										true,
										false,
										true,
										true,
										false,
										null,
										typesFound,
										null,
										null,
										null,
										false);
							}
						}
					} else {
						if ((binding.kind() & Binding.TYPE) != 0) {
							ReferenceBinding typeBinding = (ReferenceBinding) binding;
							int typeLength = token.length;

							if (!typeBinding.isStatic()) continue next;

							if (typeLength > typeBinding.sourceName.length)	continue next;

							if (isFailedMatch(token, typeBinding.sourceName)) continue next;

							int accessibility = IAccessRule.K_ACCESSIBLE;
							if(typeBinding.hasRestrictedAccess()) {
								AccessRestriction accessRestriction = this.lookupEnvironment.getAccessRestriction(typeBinding);
								if(accessRestriction != null) {
									switch (accessRestriction.getProblemId()) {
										case IProblem.ForbiddenReference:
											if (this.options.checkForbiddenReference) {
												continue next;
											}
											accessibility = IAccessRule.K_NON_ACCESSIBLE;
											break;
										case IProblem.DiscouragedReference:
											if (this.options.checkDiscouragedReference) {
												continue next;
											}
											accessibility = IAccessRule.K_DISCOURAGED;
											break;
									}
								}
							}

							if (typesFound.contains(typeBinding)) continue next;

							typesFound.add(typeBinding);

							if (this.assistNodeIsExtendedType && typeBinding.isFinal()) continue;
							if (this.assistNodeIsInterfaceExcludingAnnotation && typeBinding.isAnnotationType()) continue;
							if(this.assistNodeIsClass) {
								if(!typeBinding.isClass()) continue;
							} else if(this.assistNodeIsInterface) {
								if(!typeBinding.isInterface() && !typeBinding.isAnnotationType()) continue;
							} else if (this.assistNodeIsAnnotation) {
								if(!typeBinding.isAnnotationType()) continue;
							}

							int relevance = computeBaseRelevance();
							relevance += computeRelevanceForResolution();
							relevance += computeRelevanceForInterestingProposal(typeBinding);
							relevance += computeRelevanceForCaseMatching(token, typeBinding.sourceName);
							relevance += computeRelevanceForExpectingType(typeBinding);
							relevance += computeRelevanceForQualification(false);
							relevance += computeRelevanceForRestrictions(accessibility);

							if (typeBinding.isAnnotationType()) {
								relevance += computeRelevanceForAnnotation();
								relevance += computeRelevanceForAnnotationTarget(typeBinding);
							} else if (typeBinding.isInterface()) {
								relevance += computeRelevanceForInterface();
							} else if(typeBinding.isClass()){
								relevance += computeRelevanceForClass();
								relevance += computeRelevanceForException(typeBinding.sourceName);
							}

							if (proposeType &&
									(hasStaticMemberTypes(typeBinding, scope.enclosingSourceType(), this.unitScope) || hasArrayTypeAsExpectedSuperTypes())) {
								this.noProposal = false;
								if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
									InternalCompletionProposal proposal =  createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
									proposal.setDeclarationSignature(typeBinding.qualifiedPackageName());
									proposal.setSignature(getSignature(typeBinding));
									proposal.setPackageName(typeBinding.qualifiedPackageName());
									proposal.setTypeName(typeBinding.qualifiedSourceName());
									proposal.setCompletion(typeBinding.sourceName());
									proposal.setFlags(typeBinding.modifiers);
									proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
									proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
									proposal.setRelevance(relevance);
									this.requestor.accept(proposal);
									if(DEBUG) {
										this.printDebug(proposal);
									}
								}
							}

							findConstructorsOrAnonymousTypes(
									typeBinding,
									scope,
									FakeInvocationSite,
									false,
									relevance);
						}
					}
				}
			}
		}
	}

	private void findTypesFromStaticImports(char[] token, Scope scope, boolean proposeAllMemberTypes, ObjectVector typesFound) {
		ImportBinding[] importBindings = scope.compilationUnitScope().imports;
		if (importBindings == null) return;
		for (int i = 0; i < importBindings.length; i++) {
			ImportBinding importBinding = importBindings[i];
			if(importBinding.isValidBinding() && importBinding.isStatic()) {
				Binding binding = importBinding.resolvedImport;
				if(binding != null && binding.isValidBinding()) {
					if(importBinding.onDemand) {
						if((binding.kind() & Binding.TYPE) != 0) {
							this.findMemberTypes(
									token,
									(ReferenceBinding) binding,
									scope,
									scope.enclosingSourceType(),
									true,
									false,
									true,
									true,
									proposeAllMemberTypes,
									null,
									typesFound,
									null,
									null,
									null,
									false);
						}
					} else {
						if ((binding.kind() & Binding.TYPE) != 0) {
							ReferenceBinding typeBinding = (ReferenceBinding) binding;
							int typeLength = token.length;

							if (!typeBinding.isStatic()) continue;

							if (typeLength > typeBinding.sourceName.length)	continue;

							if (isFailedMatch(token, typeBinding.sourceName))	continue;

							if (typesFound.contains(typeBinding))  continue;

							typesFound.add(typeBinding);

							if (this.assistNodeIsExtendedType && typeBinding.isFinal()) continue;
							if (this.assistNodeIsInterfaceExcludingAnnotation && typeBinding.isAnnotationType()) continue;
							if(this.assistNodeIsClass || this.assistNodeIsException) {
								if(!typeBinding.isClass()) continue;
							} else if(this.assistNodeIsInterface) {
								if(!typeBinding.isInterface() && !typeBinding.isAnnotationType()) continue;
							} else if (this.assistNodeIsAnnotation) {
								if(!typeBinding.isAnnotationType()) continue;
							}

							int relevance = computeBaseRelevance();
							relevance += computeRelevanceForResolution();
							relevance += computeRelevanceForInterestingProposal(typeBinding);
							relevance += computeRelevanceForCaseMatching(token, typeBinding.sourceName);
							relevance += computeRelevanceForExpectingType(typeBinding);
							relevance += computeRelevanceForQualification(false);
							relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

							if (typeBinding.isClass()) {
								relevance += computeRelevanceForClass();
								relevance += computeRelevanceForException(typeBinding.sourceName);
							} else if(typeBinding.isEnum()) {
								relevance += computeRelevanceForEnum();
							} else if(typeBinding.isInterface()) {
								relevance += computeRelevanceForInterface();
							}

							this.noProposal = false;
							if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
								InternalCompletionProposal proposal =  createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
								proposal.setDeclarationSignature(typeBinding.qualifiedPackageName());
								proposal.setSignature(getSignature(typeBinding));
								proposal.setPackageName(typeBinding.qualifiedPackageName());
								proposal.setTypeName(typeBinding.qualifiedSourceName());
								proposal.setCompletion(typeBinding.sourceName());
								proposal.setFlags(typeBinding.modifiers);
								proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
					}
				}
			}
		}
	}

	private void findUnresolvedReference(int completedNameStart, int completedNameEnd, BlockScope scope, char[][] discouragedNames) {
		char[][] foundNames = findUnresolvedReferenceBefore(completedNameStart - 1, completedNameEnd, scope, discouragedNames);
		if (foundNames != null && foundNames.length > 1) {
			int discouragedNamesLength = discouragedNames.length;
			int foundNamesLength = foundNames.length;
			int newLength = discouragedNamesLength + foundNamesLength;
			System.arraycopy(discouragedNames, 0, discouragedNames = new char[newLength][], 0, discouragedNamesLength);
			System.arraycopy(foundNames, 0, discouragedNames, discouragedNamesLength, foundNamesLength);
		}
		findUnresolvedReferenceAfter(completedNameEnd + 1, scope, discouragedNames);
	}

	private char[][] findUnresolvedReferenceAfter(int from, BlockScope scope, final char[][] discouragedNames) {
		final ArrayList proposedNames = new ArrayList();

		UnresolvedReferenceNameFinder.UnresolvedReferenceNameRequestor nameRequestor =
			new UnresolvedReferenceNameFinder.UnresolvedReferenceNameRequestor() {
				@Override
				public void acceptName(char[] name) {
					CompletionEngine.this.acceptUnresolvedName(name);
					proposedNames.add(name);
				}
			};

		ReferenceContext referenceContext = scope.referenceContext();
		if (referenceContext instanceof AbstractMethodDeclaration) {
			AbstractMethodDeclaration md = (AbstractMethodDeclaration)referenceContext;

			UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
			nameFinder.findAfter(
					this.completionToken,
					md.scope,
					md.scope.classScope(),
					from,
					md.bodyEnd,
					discouragedNames,
					nameRequestor);
		} else if (referenceContext instanceof LambdaExpression) {
			LambdaExpression expression = (LambdaExpression) referenceContext;
			UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
			nameFinder.findAfter(
					this.completionToken,
					expression.scope,
					expression.scope.classScope(),
					from,
					expression.body().sourceEnd,
					discouragedNames,
					nameRequestor);
		} else if (referenceContext instanceof TypeDeclaration) {
			TypeDeclaration typeDeclaration = (TypeDeclaration) referenceContext;
			FieldDeclaration[] fields = typeDeclaration.fields;
			if (fields != null) {
				done : for (int i = 0; i < fields.length; i++) {
					if (fields[i] instanceof Initializer) {
						Initializer initializer = (Initializer) fields[i];
						if (initializer.block.sourceStart <= from &&
								from < initializer.bodyEnd) {
							UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
							nameFinder.findAfter(
										this.completionToken,
										typeDeclaration.scope,
										typeDeclaration.scope,
										from,
										initializer.bodyEnd,
										discouragedNames,
										nameRequestor);
							break done;
						}
					}
				}
			}
		}

		int proposedNamesCount = proposedNames.size();
		if (proposedNamesCount > 0) {
			return (char[][])proposedNames.toArray(new char[proposedNamesCount][]);
		}

		return null;
	}

	private char[][] findUnresolvedReferenceBefore(int recordTo, int parseTo, BlockScope scope, final char[][] discouragedNames) {
		final ArrayList proposedNames = new ArrayList();

		UnresolvedReferenceNameFinder.UnresolvedReferenceNameRequestor nameRequestor =
			new UnresolvedReferenceNameFinder.UnresolvedReferenceNameRequestor() {
				@Override
				public void acceptName(char[] name) {
					CompletionEngine.this.acceptUnresolvedName(name);
					proposedNames.add(name);
				}
			};

		BlockScope upperScope = scope;
		while (upperScope.enclosingMethodScope() != null) {
			upperScope = upperScope.enclosingMethodScope();
		}

		ReferenceContext referenceContext = upperScope.referenceContext();
		if (referenceContext instanceof AbstractMethodDeclaration) {
			AbstractMethodDeclaration md = (AbstractMethodDeclaration)referenceContext;

			UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
			nameFinder.findBefore(
					this.completionToken,
					md.scope,
					md.scope.classScope(),
					md.bodyStart,
					recordTo,
					parseTo,
					discouragedNames,
					nameRequestor);
		} else if (referenceContext instanceof TypeDeclaration) {
			TypeDeclaration typeDeclaration = (TypeDeclaration) referenceContext;


			done : {
				FieldDeclaration[] fields = typeDeclaration.fields;
				if (fields != null) {
					for (int i = 0; i < fields.length; i++) {
						if (fields[i] instanceof Initializer) {
							Initializer initializer = (Initializer) fields[i];
							if (initializer.block.sourceStart <= recordTo &&
									recordTo < initializer.bodyEnd) {

								UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
								nameFinder.findBefore(
										this.completionToken,
										typeDeclaration.scope,
										typeDeclaration.scope,
										initializer.block.sourceStart,
										recordTo,
										parseTo,
										discouragedNames,
										nameRequestor);
								break done;
							}
						}
					}
				}
			}
		}

		int proposedNamesCount = proposedNames.size();
		if (proposedNamesCount > 0) {
			return (char[][])proposedNames.toArray(new char[proposedNamesCount][]);
		}

		return null;
	}

	private void findImplementations(ProvidesStatement providesStmt, TypeReference reference ) {
		char[][] tokens = reference.getTypeName();
		char[] typeName = CharOperation.concatWithAll(tokens, '.');

		if (typeName.length == 0) {
			this.completionToken = CharOperation.ALL_PREFIX;
		} else if (reference instanceof CompletionOnProvidesImplementationsQualifiedTypeReference) {
			CompletionOnQualifiedTypeReference qReference = (CompletionOnQualifiedTypeReference) reference;
			if (qReference.completionIdentifier != null) {
				this.completionToken = CharOperation.concatAll(typeName, qReference.completionIdentifier, '.');
			}
		} else {
			 char[] lastToken = tokens[tokens.length - 1];
			 this.completionToken = lastToken != null && lastToken.length == 0 ?
					 CharOperation.concat(typeName, new char[]{'.'}) :lastToken;
		}
		setSourceRange(reference.sourceStart, reference.sourceEnd);
		findImplementations(this.completionToken, this.unitScope, providesStmt, -1);
	}

	private void findImplementations(char[] token, Scope scope, ProvidesStatement providesStmt, int stmtIndex) {
		TypeReference theInterface = providesStmt.serviceInterface;
		if (token == null) return;
		char[][] theInterfaceType = null;
		if (theInterface.resolvedType != null && theInterface.resolvedType.isValidBinding()) {
			char[] readableName = theInterface.resolvedType.readableName();
			if (readableName != null)
				theInterfaceType = CharOperation.splitOn('.', readableName);
		}
		theInterfaceType = theInterfaceType == null ? theInterface.getTypeName() : theInterfaceType;
		if (theInterfaceType == null) return;
		SearchPattern pattern  = null;
		NameEnvironmentAnswer answer =  this.nameEnvironment.findTypeInModules(theInterfaceType, scope.module());
		if (answer != null ) {
			if (answer.isSourceType()) {
				IType typeHandle = ((SourceTypeElementInfo) answer.getSourceTypes()[0]).getHandle();
				try {
					ArrayList<IType> allTypes = new ArrayList<IType>();
					ITypeHierarchy newTypeHierarchy = typeHandle.newTypeHierarchy(this.javaProject, null);
					IType[] implementingClasses = newTypeHierarchy.getImplementingClasses(typeHandle);
					for (IType iClass : implementingClasses) {
						getAllTypesInHierarchy(newTypeHierarchy,iClass,allTypes);
					}
					for (IType iType : allTypes) {
						String pkg = iType.getPackageFragment().getElementName();
						String name = iType.getElementName();
						if ( CharOperation.ALL_PREFIX != this.completionToken) {
							if(!CharOperation.prefixEquals(this.completionToken, name.toCharArray(), false))
								if(!CharOperation.prefixEquals(this.completionToken, pkg.toCharArray(), false))
									continue;
						}
						this.acceptType(pkg.toCharArray(), name.toCharArray(), CharOperation.NO_CHAR_CHAR, iType.getFlags(), null);
						acceptTypes(scope);
					}
					if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
						checkCancel();
						findPackagesInCurrentModule();
					}
					return;

				} catch (JavaModelException e) {
					//
				}
			} else if (answer.isBinaryType()) {
				String typeName = new String(CharOperation.replaceOnCopy(answer.getBinaryType().getName(), '/', '.'));
				pattern = SearchPattern.createPattern(typeName,
						IJavaSearchConstants.CLASS_AND_INTERFACE,
						IJavaSearchConstants.IMPLEMENTORS, SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE);
			}
		}
		if (pattern == null) return;
		IJavaSearchScope searchScope = BasicSearchEngine.createJavaSearchScope(new IJavaElement[] {this.javaProject});
		class ImplSearchRequestor extends SearchRequestor {
			String prefix;
			LinkedHashSet<String> filter;
			public List<IType> types = new ArrayList<>();
			public ImplSearchRequestor(char[] prefixToken, LinkedHashSet<String> filter) {
				this.prefix = (prefixToken == CharOperation.ALL_PREFIX) ? null : new String(prefixToken);
				this.filter = filter;
			}
			@Override
			public void acceptSearchMatch(SearchMatch match) throws CoreException {
			//	checkCancel();
				IJavaElement element = ((IJavaElement) match.getElement());
				if (element.getElementType() == IJavaElement.TYPE) {
					IType type = (IType) element;
					if (this.prefix != null) {
						String fullTypeName = type.getPackageFragment().getElementName();
						if (fullTypeName != null) {
							fullTypeName = fullTypeName.concat(".").concat(type.getElementName()); //$NON-NLS-1$
						} else {
							fullTypeName = type.getElementName();
						}
						if (this.filter.contains(fullTypeName)) return;
						if (!(fullTypeName.startsWith(this.prefix) || type.getElementName().startsWith(this.prefix)))
							return;
					}
					this.types.add(type);
				}
			}
		}
		try {
			LinkedHashSet<String> existingImpl = new LinkedHashSet<>();
			char[][] theInterfaceName = theInterface.getTypeName();
			// filter out existing implementations of the same interfaces
			for (int i = 0, l = this.moduleDeclaration.servicesCount; i < l; ++i) {
				if (i == stmtIndex) continue;
				ProvidesStatement prevProvides = this.moduleDeclaration.services[i];
				if (!CharOperation.equals(theInterfaceName, prevProvides.serviceInterface.getTypeName())) continue;
				TypeReference[] prevImpls = prevProvides.implementations;
				for (TypeReference prevImpl : prevImpls) {
					char[][] typeName = prevImpl.getTypeName();
					if (typeName == CharOperation.NO_CHAR_CHAR) continue;
					existingImpl.add(CharOperation.toString(typeName));
				}
			}
			// use search infrastructure - faster than using model
			ImplSearchRequestor searchRequestor = new ImplSearchRequestor(this.completionToken, existingImpl);
			new SearchEngine(this.owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/)).search(
					pattern,
					new SearchParticipant[] {SearchEngine.getDefaultSearchParticipant()},
					searchScope,
					searchRequestor,
					null
					);
			for (IType type : searchRequestor.types) {
				String pkg = type.getPackageFragment().getElementName();
				String name = type.getElementName();
				this.acceptType(pkg.toCharArray(), name.toCharArray(), CharOperation.NO_CHAR_CHAR, type.getFlags(), null);
				acceptTypes(scope);
			}
		} catch (CoreException e) {
			if (JavaModelManager.VERBOSE) {
				trace("", e); //$NON-NLS-1$
			}
		}
		if(!this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {
			checkCancel();
			findPackagesInCurrentModule();
		}
	}

	private void getAllTypesInHierarchy(ITypeHierarchy newTypeHierarchy, IType iClass, ArrayList<IType> allTypes) {
			allTypes.add(iClass);
			IType[] subclasses = newTypeHierarchy.getSubclasses(iClass);
			for (IType iType2 : subclasses) {
				getAllTypesInHierarchy(newTypeHierarchy,iType2,allTypes);

			}

	}

	private char[][] findVariableFromUnresolvedReference(LocalDeclaration variable, BlockScope scope, final char[][] discouragedNames) {
		final TypeReference type = variable.type;
		if(type != null &&
				type.resolvedType != null &&
				type.resolvedType.problemId() == ProblemReasons.NoError){

			final ArrayList proposedNames = new ArrayList();

			UnresolvedReferenceNameFinder.UnresolvedReferenceNameRequestor nameRequestor =
				new UnresolvedReferenceNameFinder.UnresolvedReferenceNameRequestor() {
					@Override
					public void acceptName(char[] name) {
						int relevance = computeBaseRelevance();
						relevance += computeRelevanceForInterestingProposal();
						relevance += computeRelevanceForCaseMatching(CompletionEngine.this.completionToken, name);
						relevance += R_NAME_FIRST_PREFIX;
						relevance += R_NAME_FIRST_SUFFIX;
						relevance += R_NAME_LESS_NEW_CHARACTERS;
						relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for variable name

						// accept result
						CompletionEngine.this.noProposal = false;
						if(!CompletionEngine.this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
							InternalCompletionProposal proposal =  CompletionEngine.this.createProposal(CompletionProposal.VARIABLE_DECLARATION, CompletionEngine.this.actualCompletionPosition);
							proposal.setSignature(getSignature(type.resolvedType));
							proposal.setPackageName(type.resolvedType.qualifiedPackageName());
							proposal.setTypeName(type.resolvedType.qualifiedSourceName());
							proposal.setName(name);
							proposal.setCompletion(name);
							//proposal.setFlags(Flags.AccDefault);
							proposal.setReplaceRange(CompletionEngine.this.startPosition - CompletionEngine.this.offset, CompletionEngine.this.endPosition - CompletionEngine.this.offset);
							proposal.setTokenRange(CompletionEngine.this.tokenStart - CompletionEngine.this.offset, CompletionEngine.this.tokenEnd - CompletionEngine.this.offset);
							proposal.setRelevance(relevance);
							CompletionEngine.this.requestor.accept(proposal);
							if(DEBUG) {
								CompletionEngine.this.printDebug(proposal);
							}
						}
						proposedNames.add(name);
					}
				};

			ReferenceContext referenceContext = scope.referenceContext();
			if (referenceContext instanceof AbstractMethodDeclaration) {
				AbstractMethodDeclaration md = (AbstractMethodDeclaration)referenceContext;

				UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
				nameFinder.find(
						this.completionToken,
						md,
						variable.declarationSourceEnd + 1,
						discouragedNames,
						nameRequestor);
			} else if (referenceContext instanceof TypeDeclaration) {
				TypeDeclaration typeDeclaration = (TypeDeclaration) referenceContext;
				FieldDeclaration[] fields = typeDeclaration.fields;
				if (fields != null) {
					done : for (int i = 0; i < fields.length; i++) {
						if (fields[i] instanceof Initializer) {
							Initializer initializer = (Initializer) fields[i];
							if (initializer.bodyStart <= variable.sourceStart &&
									variable.sourceStart < initializer.bodyEnd) {
								UnresolvedReferenceNameFinder nameFinder = new UnresolvedReferenceNameFinder(this);
								nameFinder.find(
										this.completionToken,
										initializer,
										typeDeclaration.scope,
										variable.declarationSourceEnd + 1,
										discouragedNames,
										nameRequestor);
								break done;
							}
						}
					}
				}
			}

			int proposedNamesCount = proposedNames.size();
			if (proposedNamesCount > 0) {
				return (char[][])proposedNames.toArray(new char[proposedNamesCount][]);
			}
		}

		return null;
	}

	private void findVariableName(
			char[] token,
			char[] qualifiedPackageName,
			char[] qualifiedSourceName,
			char[] sourceName,
			final TypeBinding typeBinding,
			char[][] discouragedNames,
			final char[][] forbiddenNames,
			boolean forCollection,
			int dim,
			int kind){

		if(sourceName == null || sourceName.length == 0)
			return;

		// compute variable name for non base type
		final char[] displayName;
		if (!forCollection) {
			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;
			}
		} else {
			displayName = typeBinding.qualifiedSourceName();
		}

		final char[] t = token;
		final char[] q = qualifiedPackageName;
		INamingRequestor namingRequestor = new INamingRequestor() {
			void accept(char[] name, int prefixAndSuffixRelevance, int reusedCharacters){
				int l = forbiddenNames == null ? 0 : forbiddenNames.length;
				for (int i = 0; i < l; i++) {
					if (CharOperation.equals(forbiddenNames[i], name, false)) return;
				}

				if (CharOperation.prefixEquals(t, name, false)) {
					int relevance = computeBaseRelevance();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForCaseMatching(t, name);
					relevance += prefixAndSuffixRelevance;
					if(reusedCharacters > 0) relevance += R_NAME_LESS_NEW_CHARACTERS;
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for variable name

					// accept result
					CompletionEngine.this.noProposal = false;
					if(!CompletionEngine.this.requestor.isIgnored(CompletionProposal.VARIABLE_DECLARATION)) {
						InternalCompletionProposal proposal =  CompletionEngine.this.createProposal(CompletionProposal.VARIABLE_DECLARATION, CompletionEngine.this.actualCompletionPosition);
						proposal.setSignature(getSignature(typeBinding));
						proposal.setPackageName(q);
						proposal.setTypeName(displayName);
						proposal.setName(name);
						proposal.setCompletion(name);
						//proposal.setFlags(Flags.AccDefault);
						proposal.setReplaceRange(CompletionEngine.this.startPosition - CompletionEngine.this.offset, CompletionEngine.this.endPosition - CompletionEngine.this.offset);
						proposal.setTokenRange(CompletionEngine.this.tokenStart - CompletionEngine.this.offset, CompletionEngine.this.tokenEnd - CompletionEngine.this.offset);
						proposal.setRelevance(relevance);
						CompletionEngine.this.requestor.accept(proposal);
						if(DEBUG) {
							CompletionEngine.this.printDebug(proposal);
						}
					}
				}
			}

			@Override
			public void acceptNameWithoutPrefixAndSuffix(char[] name,int reusedCharacters) {
				accept(name, 0, reusedCharacters);
			}

			@Override
			public void acceptNameWithPrefix(char[] name, boolean isFirstPrefix, int reusedCharacters) {
				accept(name, isFirstPrefix ? R_NAME_FIRST_PREFIX :  R_NAME_PREFIX, reusedCharacters);
			}

			@Override
			public void acceptNameWithPrefixAndSuffix(char[] name, boolean isFirstPrefix, boolean isFirstSuffix, int reusedCharacters) {
				accept(
						name,
						(isFirstPrefix ? R_NAME_FIRST_PREFIX : R_NAME_PREFIX) + (isFirstSuffix ? R_NAME_FIRST_SUFFIX : R_NAME_SUFFIX),
						reusedCharacters);
			}
			@Override
			public void acceptNameWithSuffix(char[] name, boolean isFirstSuffix, int reusedCharacters) {
				accept(name, isFirstSuffix ? R_NAME_FIRST_SUFFIX : R_NAME_SUFFIX, reusedCharacters);
			}
		};

		InternalNamingConventions.suggestVariableNames(
				kind,
				InternalNamingConventions.BK_SIMPLE_TYPE_NAME,
				qualifiedSourceName,
				this.javaProject,
				dim,
				token,
				discouragedNames,
				true,
				namingRequestor);
	}

	// Helper method for private void findVariableNames(char[] name, TypeReference type )
	private void findVariableName(
			char[] token,
			char[] qualifiedPackageName,
			char[] qualifiedSourceName,
			char[] sourceName,
			final TypeBinding typeBinding,
			char[][] discouragedNames,
			final char[][] forbiddenNames,
			int dim,
			int kind){
		findVariableName(
				token,
				qualifiedPackageName,
				qualifiedSourceName,
				sourceName,
				typeBinding,
				discouragedNames,
				forbiddenNames,
				false,
				dim,
				kind);
	}
	private void findVariableNameForCollection(
			char[] token,
			char[] qualifiedPackageName,
			char[] qualifiedSourceName,
			char[] sourceName,
			final TypeBinding typeBinding,
			char[][] discouragedNames,
			final char[][] forbiddenNames,
			int kind){

		findVariableName(
				token,
				qualifiedPackageName,
				qualifiedSourceName,
				sourceName,
				typeBinding,
				discouragedNames,
				forbiddenNames,
				false,
				1,
				kind);
	}
	private void findVariableNames(char[] name, TypeReference type , char[][] discouragedNames, char[][] forbiddenNames, int kind){
		if(type != null &&
			type.resolvedType != null) {
			TypeBinding tb = type.resolvedType;

			if (tb.problemId() == ProblemReasons.NoError &&
					TypeBinding.notEquals(tb, Scope.getBaseType(VOID))) {
				findVariableName(
					name,
					tb.leafComponentType().qualifiedPackageName(),
					tb.leafComponentType().qualifiedSourceName(),
					tb.leafComponentType().sourceName(),
					tb,
					discouragedNames,
					forbiddenNames,
					type.dimensions(),
					kind);

				if (tb.isParameterizedType() &&
						tb.findSuperTypeOriginatingFrom(TypeIds.T_JavaUtilCollection, false) != null) {
					ParameterizedTypeBinding ptb = ((ParameterizedTypeBinding) tb);
					TypeBinding[] arguments = ptb.arguments;
					if (arguments != null && arguments.length == 1) {
						TypeBinding argument = arguments[0];
						findVariableNameForCollection(
							name,
							argument.leafComponentType().qualifiedPackageName(),
							argument.leafComponentType().qualifiedSourceName(),
							argument.leafComponentType().sourceName(),
							tb,
							discouragedNames,
							forbiddenNames,
							kind);
					}
				}
			}
		}

	}
	private void findVariablesAndMethods(
		char[] token,
		Scope scope,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean insideTypeAnnotation,
		boolean insideAnnotationAttribute) {
		findVariablesAndMethods(token, scope, invocationSite, invocationScope, insideTypeAnnotation, insideAnnotationAttribute, true, new ObjectVector());
	}
	private void findVariablesAndMethods(
		char[] token,
		Scope scope,
		InvocationSite invocationSite,
		Scope invocationScope,
		boolean insideTypeAnnotation,
		boolean insideAnnotationAttribute,
		boolean canBePrefixed,
		ObjectVector methodsFound) {

		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();

		Scope currentScope = scope;

		char[]	boostMatches = findParameterNameAtLocationFromAssistParent(methodsFound, scope);

		if (!this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
			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;

					//$FALL-THROUGH$
					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 (isFailedMatch(token, local.name))
								continue next;

							if (local.isSecret())
								continue next;

							// https://bugs.eclipse.org/bugs/show_bug.cgi?id=328674
							if (local.declaration.initialization != null && !local.declaration.type.isTypeNameVar(null)) {
								// proposal being asked inside field's initialization. Don't propose this field.
								continue next;
							}

							// don't propose non constant variables or strings (1.6 or below) in case expression
							// https://bugs.eclipse.org/bugs/show_bug.cgi?id=195346
							// https://bugs.eclipse.org/bugs/show_bug.cgi?id=343342
							if (this.assistNodeIsInsideCase) {
								if (local.isFinal()) {
									if (this.assistNodeIsString){
										if (local.type == null || local.type.id != TypeIds.T_JavaLangString)
											continue next;
									} else if (!(local.type instanceof BaseTypeBinding))
										continue next;
								} else {
									continue next; // non-constants not allowed in case.
								}
							}

							int ptr = this.uninterestingBindingsPtr;
							// Cases where the binding is uninteresting eg. for completion occurring inside a local var
							// declaration, the local var binding is uninteresting and shouldn't be proposed.
							while (ptr >= 0) {
								if (this.uninterestingBindings[ptr] == local) {
									continue next;
								}
								ptr--;
							}

							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 = computeBaseRelevance();
							relevance += computeRelevanceForResolution();
							relevance += computeRelevanceForInterestingProposal(local);
							relevance += computeRelevanceForCaseMatching(token, local.name);
							if(boostMatches.length > 0) {
								relevance += computeRelevanceForCaseMatching(boostMatches, local.name);
							}
							int computeRelevanceForExpectingType = computeRelevanceForExpectingType(local.type);
							if(this.strictMatchForExtepectedType && computeRelevanceForExpectingType <= 0) {
								continue;
							} else if(!this.strictMatchForExtepectedType) {
								relevance += computeRelevanceForExpectingType;
							}

							relevance += computeRelevanceForEnumConstant(local.type);
							relevance += computeRelevanceForQualification(false);
							relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for local variable
							relevance += computeRelevanceForFinal(this.assistNodeIsInsideCase, local.isFinal());
							this.noProposal = false;
							if(!this.requestor.isIgnored(CompletionProposal.LOCAL_VARIABLE_REF)) {
								InternalCompletionProposal proposal =  createProposal(CompletionProposal.LOCAL_VARIABLE_REF, this.actualCompletionPosition);
								proposal.setSignature(
									local.type == null
									? createTypeSignature(
											CharOperation.NO_CHAR,
											local.declaration.type.toString().toCharArray())
									: getSignature(local.type));
								if(local.type == null) {
									//proposal.setPackageName(null);
									proposal.setTypeName(local.declaration.type.toString().toCharArray());
								} else {
									proposal.setPackageName(local.type.qualifiedPackageName());
									proposal.setTypeName(local.type.qualifiedSourceName());
								}
								proposal.setName(local.name);
								proposal.setCompletion(local.name);
								proposal.setFlags(local.modifiers);
								proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
								proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
								proposal.setRelevance(relevance);
								this.requestor.accept(proposal);
								if(DEBUG) {
									this.printDebug(proposal);
								}
							}
						}
						break;

					case Scope.COMPILATION_UNIT_SCOPE :
						break done1;
				}
				currentScope = currentScope.parent;
			}
		}

		checkCancel();

		boolean proposeField = !this.requestor.isIgnored(CompletionProposal.FIELD_REF);
		boolean proposeMethod = !this.requestor.isIgnored(CompletionProposal.METHOD_REF);

		staticsOnly = false;
		currentScope = scope;

		if(proposeField || proposeMethod) {
			done2 : 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;
						break;
					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 { */
						if(!insideTypeAnnotation) {
							if(proposeField) {
								findFields(
									token,
									enclosingType,
									classScope,
									fieldsFound,
									localsFound,
									staticsOnly,
									invocationSite,
									invocationScope,
									true,
									canBePrefixed,
									null,
									null,
									null,
									false,
									null,
									-1,
									-1);
							}
							if(proposeMethod && !insideAnnotationAttribute) {
								findMethods(
									token,
									null,
									null,
									enclosingType,
									classScope,
									methodsFound,
									staticsOnly,
									false,
									invocationSite,
									invocationScope,
									true,
									false,
									canBePrefixed,
									null,
									null,
									null,
									false,
									null,
									-1,
									-1);
							}
						}
						staticsOnly |= enclosingType.isStatic();
						insideTypeAnnotation = false;
						//				}
						break;

					case Scope.COMPILATION_UNIT_SCOPE :
						break done2;
				}
				currentScope = currentScope.parent;
			}

			checkCancel();

			findFieldsAndMethodsFromStaticImports(
					token,
					scope,
					invocationSite,
					invocationScope,
					false,
					insideAnnotationAttribute,
					localsFound,
					fieldsFound,
					methodsFound,
					proposeField,
					proposeMethod);

			if (this.assistNodeInJavadoc == 0) {

				checkCancel();

				// search in favorites import
				findFieldsAndMethodsFromFavorites(
						token,
						scope,
						invocationSite,
						invocationScope,
						localsFound,
						fieldsFound,
						methodsFound);
			}

			checkCancel();

			findEnumConstantsFromExpectedTypes(
					token,
					invocationScope,
					fieldsFound);

			findLambdaExpressions(currentScope);
		}
	}

	private char[] getCompletedTypeSignature(ReferenceBinding referenceBinding) {
		char[] result = null;
		StringBuilder sig = new StringBuilder(10);
		if (!referenceBinding.isMemberType()) {
			char[] typeSig = referenceBinding.genericTypeSignature();
			sig.append(typeSig, 0, typeSig.length);
		} else if (!this.insideQualifiedReference) {
			if (referenceBinding.isStatic()) {
				char[] typeSig = referenceBinding.signature();
				sig.append(typeSig, 0, typeSig.length-1); // copy all but trailing semicolon

				TypeVariableBinding[] typeVariables = referenceBinding.typeVariables();
				if (typeVariables != Binding.NO_TYPE_VARIABLES) {
				    sig.append(Signature.C_GENERIC_START);
				    for (int i = 0, length = typeVariables.length; i < length; i++) {
				        sig.append(typeVariables[i].genericTypeSignature());
				    }
				    sig.append(Signature.C_GENERIC_END);
				}
				sig.append(Signature.C_SEMICOLON);
			} else {
				char[] typeSig = referenceBinding.genericTypeSignature();
				sig.append(typeSig, 0, typeSig.length);
			}
		} else {
			ReferenceBinding enclosingType = referenceBinding.enclosingType();
			if (enclosingType.isParameterizedType()) {
				char[] typeSig = referenceBinding.genericTypeSignature();
				sig.append(typeSig, 0, typeSig.length-1);

				TypeVariableBinding[] typeVariables = referenceBinding.typeVariables();
				if (typeVariables != Binding.NO_TYPE_VARIABLES) {
				    sig.append(Signature.C_GENERIC_START);
				    for (int i = 0, length = typeVariables.length; i < length; i++) {
				        sig.append(typeVariables[i].genericTypeSignature());
				    }
				    sig.append(Signature.C_GENERIC_END);
				}
			} else {
				char[] typeSig = referenceBinding.signature();
				sig.append(typeSig, 0, typeSig.length-1); // copy all but trailing semicolon

				if (referenceBinding.isStatic()) {
					TypeVariableBinding[] typeVariables = referenceBinding.typeVariables();
					if (typeVariables != Binding.NO_TYPE_VARIABLES) {
					    sig.append(Signature.C_GENERIC_START);
					    for (int i = 0, length = typeVariables.length; i < length; i++) {
					        sig.append(typeVariables[i].genericTypeSignature());
					    }
					    sig.append(Signature.C_GENERIC_END);
					}
				}
			}
			sig.append(Signature.C_SEMICOLON);
		}
		int sigLength = sig.length();
		result = new char[sigLength];
		sig.getChars(0, sigLength, result, 0);
		result = CharOperation.replaceOnCopy(result, '/', Signature.C_DOT);
		if (Engine.isAnonymousClassSignature(result)) {
			result = null;
		}
		return result;
	}

	private ImportBinding[] getFavoriteReferenceBindings(Scope scope) {
		if (this.favoriteReferenceBindings != null) return this.favoriteReferenceBindings;

		String[] favoriteReferences = this.requestor.getFavoriteReferences();

		if (favoriteReferences == null || favoriteReferences.length == 0) return null;

		ImportBinding[] resolvedImports = new ImportBinding[favoriteReferences.length];

		int count = 0;
		next : for (int i = 0; i < favoriteReferences.length; i++) {
			String favoriteReference = favoriteReferences[i];

			int length;
			if (favoriteReference == null || (length = favoriteReference.length()) == 0) continue next;

			boolean onDemand = favoriteReference.charAt(length - 1) == '*';

			char[][] compoundName = CharOperation.splitOn('.', favoriteReference.toCharArray());
			if (onDemand) {
				compoundName = CharOperation.subarray(compoundName, 0, compoundName.length - 1);
			}

			// remove duplicate and conflicting
			for (int j = 0; j < count; j++) {
				ImportReference f = resolvedImports[j].reference;

				if (CharOperation.equals(f.tokens, compoundName)) continue next;

				if (!onDemand && ((f.bits & ASTNode.OnDemand) == 0)) {
					if (CharOperation.equals(f.tokens[f.tokens.length - 1], compoundName[compoundName.length - 1]))
						continue next;
				}
			}

			boolean isStatic = true;

			ImportReference importReference =
				new ImportReference(
						compoundName,
						new long[compoundName.length],
						onDemand,
						isStatic ? ClassFileConstants.AccStatic : ClassFileConstants.AccDefault);

			Binding importBinding = this.unitScope.findImport(compoundName, isStatic, onDemand);

			if (!importBinding.isValidBinding()) {
				continue next;
			}

			if (importBinding instanceof PackageBinding) {
				continue next;
			}

			resolvedImports[count++] =
				new ImportBinding(compoundName, onDemand, importBinding, importReference);
		}

		if (resolvedImports.length > count)
			System.arraycopy(resolvedImports, 0, resolvedImports = new ImportBinding[count], 0, count);

		return this.favoriteReferenceBindings = resolvedImports;
	}

	private INameEnvironment getNoCacheNameEnvironment() {
		if (this.noCacheNameEnvironment == null) {
			JavaModelManager.getJavaModelManager().cacheZipFiles(this);
			this.noCacheNameEnvironment = new JavaSearchNameEnvironment(this.javaProject, this.owner == null ? null : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/));
		}
		return this.noCacheNameEnvironment;
	}

	@Override
	public AssistParser getParser() {

		return this.parser;
	}
	protected boolean hasArrayTypeAsExpectedSuperTypes() {
		if ((this.expectedTypesFilter & ~SUBTYPE) != 0) return false;

		if (!this.hasComputedExpectedArrayTypes) {
			if(this.expectedTypes != null) {
				done : for (int i = 0; i <= this.expectedTypesPtr; i++) {
					if(this.expectedTypes[i].isArrayType()) {
						this.hasExpectedArrayTypes = true;
						break done;
					}
				}
			}

			this.hasComputedExpectedArrayTypes = true;
		}

		return this.hasExpectedArrayTypes;
	}
	protected boolean hasPossibleAnnotationTarget(TypeBinding typeBinding, Scope scope) {
		if (this.targetedElement == TagBits.AnnotationForPackage) {
			long target = typeBinding.getAnnotationTagBits() & TagBits.AnnotationTargetMASK;
			if(target != 0 && (target & TagBits.AnnotationForPackage) == 0) {
				return false;
			}
		} else if (this.targetedElement == TagBits.AnnotationForModule) {
			long target = typeBinding.getAnnotationTagBits() & TagBits.AnnotationTargetMASK;
			if(target != 0 && (target & TagBits.AnnotationForModule) == 0) {
				return false;
			}
		} else if ((this.targetedElement & (TagBits.AnnotationForType | TagBits.AnnotationForTypeUse)) != 0) {
			if (scope.parent != null &&
					scope.parent.parent != null &&
					scope.parent.referenceContext() instanceof CompletionOnAnnotationOfType &&
					scope.parent.parent instanceof CompilationUnitScope) {
				long target = typeBinding.getAnnotationTagBits() & TagBits.AnnotationTargetMASK;
				if ((this.targetedElement & TagBits.AnnotationForAnnotationType) != 0) {
					if(target != 0 && (target &(TagBits.AnnotationForType | TagBits.AnnotationForAnnotationType | TagBits.AnnotationForTypeUse)) == 0) {
						return false;
					}
				} else {
					if (target != 0 && (target & (TagBits.AnnotationForType | TagBits.AnnotationForTypeUse)) == 0) {
						return false;
					}
				}
			}
		}
		return true;
	}
	/**
	 * Returns completion string inserted inside a specified inline tag.
	 * @return char[] Completion text inclunding specified inline tag
	 */
	private char[] inlineTagCompletion(char[] completionName, char[] inlineTag) {
		int tagLength= inlineTag.length;
		int completionLength = completionName.length;
		int inlineLength = 2+tagLength+1+completionLength+1;
		char[] inlineCompletion = new char[inlineLength];
		inlineCompletion[0] = '{';
		inlineCompletion[1] = '@';
		System.arraycopy(inlineTag, 0, inlineCompletion, 2, tagLength);
		inlineCompletion[tagLength+2] = ' ';
		System.arraycopy(completionName, 0, inlineCompletion, tagLength+3, completionLength);
		// do not add space at end of inline tag (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=121026)
		//inlineCompletion[inlineLength-2] = ' ';
		inlineCompletion[inlineLength-1] = '}';
		return inlineCompletion;
	}
	private boolean isAllowingLongComputationProposals() {
		return this.monitor != null;
	}

	/**
	 * Checks whether name matches the token according to the current
	 * code completion settings (substring match, camel case match etc.)
	 * and sets whether the current match is a suffix proposal.
	 *
	 * @param token the token that is tested
	 * @param name the name to match
	 * @return <code>true</code> if the token does not match,
	 * <code>false</code> otherwise
	 */
	private boolean isFailedMatch(char[] token, char[] name) {
		if ((this.options.substringMatch && CharOperation.substringMatch(token, name))
				|| (this.options.camelCaseMatch && CharOperation.camelCaseMatch(token, name))
				|| CharOperation.prefixEquals(token, name, false)
				|| (this.options.subwordMatch && CharOperation.subWordMatch(token, name))) {
			return false;
		}

		return true;
	}
	private boolean isForbidden(ReferenceBinding binding) {
		for (int i = 0; i <= this.forbbidenBindingsPtr; i++) {
			if(this.forbbidenBindings[i] == binding) {
				return true;
			}
		}
		if (!isValidPackageName(binding.qualifiedPackageName())) {
			return true;
		}
		return false;
	}

	private boolean isForbidden(char[] givenPkgName, char[] givenTypeName, char[][] enclosingTypeNames) {
		// CharOperation.concatWith() handles the cases where input args are null/empty
		char[] fullTypeName = CharOperation.concatWith(enclosingTypeNames, givenTypeName, '.');
		for (int i = 0; i <= this.forbbidenBindingsPtr; i++) {
			if (this.forbbidenBindings[i] instanceof TypeBinding) {
				TypeBinding typeBinding = (TypeBinding) this.forbbidenBindings[i];
				char[] currPkgName = typeBinding.qualifiedPackageName();
				if (CharOperation.equals(givenPkgName, currPkgName))	{
					char[] currTypeName = typeBinding.qualifiedSourceName();
					if (CharOperation.equals(fullTypeName, currTypeName)) {
						return true;
					}
				}
			}
		}

		if (!isValidPackageName(givenPkgName)) {
			return true;
		}

		return false;
	}

	private boolean isIgnored(int kind) {
		return this.requestor.isIgnored(kind);
	}
	boolean isIgnored(int kind, boolean missingTypes) {
		return this.requestor.isIgnored(kind) ||
			(missingTypes && !this.requestor.isAllowingRequiredProposals(kind, CompletionProposal.TYPE_REF));
	}

	private boolean isIgnored(int kind, int requiredProposalKind) {
		return this.requestor.isIgnored(kind) ||
			!this.requestor.isAllowingRequiredProposals(kind, requiredProposalKind);
	}

	private boolean isValidPackageName(char[] packageName) {
		if (this.validPackageNames.includes(packageName)) {
			return true;
		}

		if (this.invalidPackageNames.includes(packageName)) {
			return false;
		}

		char[][] names = CharOperation.splitOn('.', packageName);
		for (int i = 0, length = names.length; i < length; i++) {
			if (!Util.isValidFolderNameForPackage(new String(names[i]), this.sourceLevel, this.complianceLevel)) {
				this.invalidPackageNames.add(packageName);
				return false;
			}
		}

		this.validPackageNames.add(packageName);
		return true;
	}

	private boolean isValidParent(ASTNode parent, ASTNode node, Scope scope){

		if(parent instanceof ParameterizedSingleTypeReference) {
			ParameterizedSingleTypeReference ref = (ParameterizedSingleTypeReference) parent;
			TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
			int length = ref.typeArguments == null ? 0 : ref.typeArguments.length;
			int nodeIndex = -1;
			for(int i = length - 1 ; i > -1 ; i--) {
				if(node == ref.typeArguments[i]) {
					nodeIndex = i;
					break;
				}
			}
			if(nodeIndex > -1 && (typeVariables == null || typeVariables.length < nodeIndex + 1)) {
				TypeBinding[] typeBindings = new TypeBinding[nodeIndex + 1];
				for(int i = 0; i < nodeIndex; i++) {
					typeBindings[i] = ref.typeArguments[i].resolvedType;
				}
				typeBindings[nodeIndex] = scope.getJavaLangObject();
				if(typeVariables == null || typeVariables.length == 0) {
					scope.problemReporter().nonGenericTypeCannotBeParameterized(0, ref, ref.resolvedType, typeBindings);
				} else {
					scope.problemReporter().incorrectArityForParameterizedType(ref, ref.resolvedType, typeBindings);
				}
				return false;
			}
		} else if(parent instanceof ParameterizedQualifiedTypeReference) {
			ParameterizedQualifiedTypeReference ref = (ParameterizedQualifiedTypeReference) parent;
			TypeVariableBinding[] typeVariables = ((ReferenceBinding)ref.resolvedType).typeVariables();
			TypeReference[][] arguments = ref.typeArguments;
			int iLength = arguments == null ? 0 : arguments.length;
			for (int i = 0; i < iLength; i++) {
				int jLength = arguments[i] == null ? 0 : arguments[i].length;
				for (int j = 0; j < jLength; j++) {
					if(arguments[i][j] == node && (typeVariables == null || typeVariables.length <= j)) {
						TypeBinding[] typeBindings = new TypeBinding[j + 1];
						for(int k = 0; k < j; k++) {
							typeBindings[k] = ref.typeArguments[i][k].resolvedType;
						}
						typeBindings[j] = scope.getJavaLangObject();
						if(typeVariables == null || typeVariables.length == 0) {
							scope.problemReporter().nonGenericTypeCannotBeParameterized(0, ref, ref.resolvedType, typeBindings);
						} else {
							scope.problemReporter().incorrectArityForParameterizedType(ref, ref.resolvedType, typeBindings);
						}
						return false;
					}
				}
			}
		} else if (parent instanceof QualifiedAllocationExpression) {
			Expression enclosingInstance = ((QualifiedAllocationExpression) parent).enclosingInstance();
			if (enclosingInstance instanceof AllocationExpression) {
				AllocationExpression enclosingAllocation = (AllocationExpression) enclosingInstance;
				if (enclosingAllocation.type != null && (enclosingAllocation.type.bits & ASTNode.IsDiamond) != 0) {
					if (enclosingAllocation.binding != null) {
						for (TypeBinding param : enclosingAllocation.binding.original().parameters) {
							if (param.kind() == Binding.TYPE_PARAMETER)
								return true; // this could possible help type inference, let's assume the best
						}
						scope.problemReporter().cannotInferElidedTypes(enclosingAllocation);
						return false; // will fail type inference, hence no useful proposals can be made
					} else if (enclosingAllocation.arguments == null) {
						scope.problemReporter().cannotInferElidedTypes(enclosingAllocation);
						return false; // will fail type inference, hence no useful proposals can be made
					}
				}
			}
		}
		return true;
	}
	private boolean mustQualifyType(ReferenceBinding type, char[] packageName, Scope scope) {
		if(!mustQualifyType(
				packageName,
				type.sourceName(),
				type.isMemberType() ? type.enclosingType().qualifiedSourceName() : null,
				type.modifiers)) {
			return false;
		}
		ReferenceBinding enclosingType = scope.enclosingSourceType();
		while (enclosingType != null) {
			ReferenceBinding currentType = enclosingType;
			while (currentType != null) {
				ReferenceBinding[] memberTypes = currentType.memberTypes();
				if(memberTypes != null) {
					for (int i = 0; i < memberTypes.length; i++) {
						if (CharOperation.equals(memberTypes[i].sourceName, type.sourceName()) &&
								memberTypes[i].canBeSeenBy(scope)) {
							return TypeBinding.notEquals(memberTypes[i], type);
						}
					}
				}
				currentType = currentType.superclass();
			}
			enclosingType = enclosingType.enclosingType();
		}
		return true;
	}
	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++) {
			ASTNode.printModifiers(localVariableModifiers[i], prefix);
			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$
		this.offset = prefix.length();

		String encoding = this.compilerOptions.defaultEncoding;
		@SuppressWarnings("deprecation")
		BasicCompilationUnit fakeUnit = new BasicCompilationUnit(
			fakeSource,
			null,
			"FakeType.java", //$NON-NLS-1$
			encoding);

		this.actualCompletionPosition = prefix.length() + position - 1;

		CompilationResult fakeResult = new CompilationResult(fakeUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit);
		CompilationUnitDeclaration fakeAST = this.parser.dietParse(fakeUnit, fakeResult, this.actualCompletionPosition);

		parseBlockStatements(fakeAST, this.actualCompletionPosition);

		return (Initializer)fakeAST.types[0].fields[0];
	}
	protected void printDebug(CategorizedProblem error) {
		if(CompletionEngine.DEBUG) {
			trace("COMPLETION - completionFailure(" + error + ")"); //$NON-NLS-1$ //$NON-NLS-2$
		}
	}
	protected void printDebug(CompletionProposal proposal){
		StringBuffer buffer = new StringBuffer();
		printDebug(proposal, 0, buffer);
		trace(buffer.toString());
	}

	private void printDebug(CompletionProposal proposal, int tab, StringBuffer buffer){
		printDebugTab(tab, buffer);
		buffer.append("COMPLETION - "); //$NON-NLS-1$
		switch(proposal.getKind()) {
			case CompletionProposal.ANONYMOUS_CLASS_DECLARATION :
				buffer.append("ANONYMOUS_CLASS_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.FIELD_REF :
				buffer.append("FIELD_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER :
				buffer.append("FIELD_REF_WITH_CASTED_RECEIVER"); //$NON-NLS-1$
				break;
			case CompletionProposal.KEYWORD :
				buffer.append("KEYWORD"); //$NON-NLS-1$
				break;
			case CompletionProposal.LABEL_REF :
				buffer.append("LABEL_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.LOCAL_VARIABLE_REF :
				buffer.append("LOCAL_VARIABLE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_DECLARATION :
				buffer.append("METHOD_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_REF :
				buffer.append("METHOD_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER :
				buffer.append("METHOD_REF_WITH_CASTED_RECEIVER"); //$NON-NLS-1$
				break;
			case CompletionProposal.MODULE_DECLARATION :
				buffer.append("MODULE_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.MODULE_REF :
				buffer.append("MODULE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.PACKAGE_REF :
				buffer.append("PACKAGE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.TYPE_REF :
				buffer.append("TYPE_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.VARIABLE_DECLARATION :
				buffer.append("VARIABLE_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.POTENTIAL_METHOD_DECLARATION :
				buffer.append("POTENTIAL_METHOD_DECLARATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_NAME_REFERENCE :
				buffer.append("METHOD_NAME_REFERENCE"); //$NON-NLS-1$
				break;
			case CompletionProposal.ANNOTATION_ATTRIBUTE_REF :
				buffer.append("ANNOTATION_ATTRIBUT_REF"); //$NON-NLS-1$
				break;
			case CompletionProposal.FIELD_IMPORT :
				buffer.append("FIELD_IMPORT"); //$NON-NLS-1$
				break;
			case CompletionProposal.METHOD_IMPORT :
				buffer.append("METHOD_IMPORT"); //$NON-NLS-1$
				break;
			case CompletionProposal.TYPE_IMPORT :
				buffer.append("TYPE_IMPORT"); //$NON-NLS-1$
				break;
			case CompletionProposal.CONSTRUCTOR_INVOCATION :
				buffer.append("CONSTRUCTOR_INVOCATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION :
				buffer.append("ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION"); //$NON-NLS-1$
				break;
			case CompletionProposal.LAMBDA_EXPRESSION :
				buffer.append("LAMBDA_EXPRESSION"); //$NON-NLS-1$
				break;
			default :
				buffer.append("PROPOSAL"); //$NON-NLS-1$
				break;

		}

		buffer.append("{\n");//$NON-NLS-1$
		printDebugTab(tab, buffer);
		buffer.append("\tCompletion[").append(proposal.getCompletion() == null ? "null".toCharArray() : proposal.getCompletion()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		printDebugTab(tab, buffer);
		buffer.append("\tDeclarationSignature[").append(proposal.getDeclarationSignature() == null ? "null".toCharArray() : proposal.getDeclarationSignature()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		printDebugTab(tab, buffer);
		buffer.append("\tDeclarationKey[").append(proposal.getDeclarationKey() == null ? "null".toCharArray() : proposal.getDeclarationKey()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		printDebugTab(tab, buffer);
		buffer.append("\tSignature[").append(proposal.getSignature() == null ? "null".toCharArray() : proposal.getSignature()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		printDebugTab(tab, buffer);
		buffer.append("\tKey[").append(proposal.getKey() == null ? "null".toCharArray() : proposal.getKey()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		printDebugTab(tab, buffer);
		buffer.append("\tName[").append(proposal.getName() == null ? "null".toCharArray() : proposal.getName()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

		printDebugTab(tab, buffer);
		buffer.append("\tFlags[");//$NON-NLS-1$
		int flags = proposal.getFlags();
		buffer.append(Flags.toString(flags));
		if((flags & Flags.AccInterface) != 0) buffer.append("interface ");//$NON-NLS-1$
		if((flags & Flags.AccEnum) != 0) buffer.append("enum ");//$NON-NLS-1$
		buffer.append("]\n"); //$NON-NLS-1$

		CompletionProposal[] proposals = proposal.getRequiredProposals();
		if(proposals != null) {
			printDebugTab(tab, buffer);
			buffer.append("\tRequiredProposals[");//$NON-NLS-1$
			for (int i = 0; i < proposals.length; i++) {
				buffer.append("\n"); //$NON-NLS-1$
				printDebug(proposals[i], tab + 2, buffer);
			}
			printDebugTab(tab, buffer);
			buffer.append("\n\t]\n"); //$NON-NLS-1$
		}

		printDebugTab(tab, buffer);
		buffer.append("\tCompletionLocation[").append(proposal.getCompletionLocation()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		int start = proposal.getReplaceStart();
		int end = proposal.getReplaceEnd();
		printDebugTab(tab, buffer);
		buffer.append("\tReplaceStart[").append(start).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("-ReplaceEnd[").append(end).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		start = proposal.getTokenStart();
		end = proposal.getTokenEnd();
		printDebugTab(tab, buffer);
		buffer.append("\tTokenStart[").append(start).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("-TokenEnd[").append(end).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		if (this.source != null) {
			printDebugTab(tab, buffer);
			buffer.append("\tReplacedText[").append(this.source, start, end-start).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		}
		printDebugTab(tab, buffer);
		buffer.append("\tTokenStart[").append(proposal.getTokenStart()).append("]"); //$NON-NLS-1$ //$NON-NLS-2$
		buffer.append("-TokenEnd[").append(proposal.getTokenEnd()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$
		printDebugTab(tab, buffer);
		buffer.append("\tRelevance[").append(proposal.getRelevance()).append("]\n"); //$NON-NLS-1$ //$NON-NLS-2$

		printDebugTab(tab, buffer);
		buffer.append("}\n");//$NON-NLS-1$
	}

	private void printDebugTab(int tab, StringBuffer buffer) {
		for (int i = 0; i < tab; i++) {
			buffer.append('\t');
		}
	}

	private void proposeConstructor(AcceptedConstructor deferredProposal, Scope scope) {
		if (deferredProposal.proposeConstructor) {
			proposeConstructor(
					deferredProposal.simpleTypeName,
					deferredProposal.parameterCount,
					deferredProposal.signature,
					deferredProposal.parameterTypes,
					deferredProposal.parameterNames,
					deferredProposal.modifiers,
					deferredProposal.packageName,
					deferredProposal.typeModifiers,
					deferredProposal.accessibility,
					deferredProposal.simpleTypeName,
					deferredProposal.fullyQualifiedName,
					deferredProposal.mustBeQualified,
					scope,
					deferredProposal.extraFlags);
		}
	}

	private void proposeConstructor(
			char[] simpleTypeName,
			int parameterCount,
			char[] signature,
			char[][] parameterTypes,
			char[][] parameterNames,
			int modifiers,
			char[] packageName,
			int typeModifiers,
			int accessibility,
			char[] typeName,
			char[] fullyQualifiedName,
			boolean isQualified,
			Scope scope,
			int extraFlags) {
		char[] typeCompletion = fullyQualifiedName;
		if(isQualified) {
			if (packageName == null || packageName.length == 0)
				if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
					return; // ignore types from the default package from outside it
		} else {
			typeCompletion = simpleTypeName;
		}

		if (this.requestor.isIgnored(fullyQualifiedName)) {
			return;
		}

		ReferenceBinding typeBinding = this.lookupEnvironment.getType(CharOperation.splitOn('.', fullyQualifiedName));

		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal();
		relevance += computeRelevanceForRestrictions(accessibility);
		relevance += computeRelevanceForCaseMatching(this.completionToken, simpleTypeName);
		int relevanceForExpected = computeRelevanceForExpectingType(typeBinding);
		relevance += relevanceForExpected;
		relevance += computeRelevanceForQualification(isQualified);
		relevance += computeRelevanceForConstructor();

		boolean isCompatibleType = this.expectedTypesPtr < 0 || relevanceForExpected >= R_EXPECTED_TYPE;
		char[][] typeVariables = null;
		if (typeBinding != null && typeBinding.typeVariables() != null) {
			TypeVariableBinding[] variableBindings = typeBinding.erasure().typeVariables();
			typeVariables = new char[variableBindings.length][];
			for (int i = 0; i < typeVariables.length; i++) {
				typeVariables[i] = variableBindings[i].sourceName;
			}
		}

		boolean isInterface = false;
		int kind = typeModifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation);
		switch (kind) {
			case ClassFileConstants.AccAnnotation:
			case ClassFileConstants.AccAnnotation | ClassFileConstants.AccInterface:
				relevance += computeRelevanceForAnnotation();
				relevance += computeRelevanceForInterface();
				isInterface = true;
				break;
			case ClassFileConstants.AccEnum:
				relevance += computeRelevanceForEnum();
				break;
			case ClassFileConstants.AccInterface:
				relevance += computeRelevanceForInterface();
				isInterface = true;
				break;
			default:
				relevance += computeRelevanceForClass();
				relevance += computeRelevanceForException(simpleTypeName);
				break;
		}

		char[] completion;
		if (this.source != null
					&& this.source.length > this.endPosition
					&& this.source[this.endPosition] == '(') {
			completion = CharOperation.NO_CHAR;
		} else {
			completion = new char[] { '(', ')' };
		}

		InternalCompletionProposal typeProposal = createProposal(CompletionProposal.TYPE_REF, this.actualCompletionPosition);
		typeProposal.nameLookup = this.nameEnvironment.nameLookup;
		typeProposal.completionEngine = this;
		typeProposal.setDeclarationSignature(packageName);
		typeProposal.setSignature(createNonGenericTypeSignature(packageName, typeName));
		typeProposal.setPackageName(packageName);
		typeProposal.setTypeName(typeName);
		typeProposal.setCompletion(typeCompletion);
		typeProposal.setFlags(typeModifiers);
		typeProposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
		typeProposal.setTokenRange(this.startPosition - this.offset, this.endPosition - this.offset);
		typeProposal.setRelevance(relevance);
		typeProposal.setDeclarationTypeVariables(typeVariables);
		typeProposal.setCompatibleProposal(isCompatibleType);

		switch (parameterCount) {
			case -1: // default constructor
				int flags = Flags.AccPublic;
				if (Flags.isDeprecated(typeModifiers)) {
					flags |= Flags.AccDeprecated;
				}

				if (isInterface || (typeModifiers & ClassFileConstants.AccAbstract) != 0) {
					this.noProposal = false;
					if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
						InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
						proposal.setDeclarationSignature(createNonGenericTypeSignature(packageName, typeName));
						proposal.setDeclarationKey(createBindingKey(packageName, typeName));
						proposal.setSignature(DEFAULT_CONSTRUCTOR_SIGNATURE);
						proposal.setDeclarationPackageName(packageName);
						proposal.setDeclarationTypeName(typeName);
						proposal.setParameterPackageNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterTypeNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterNames(CharOperation.NO_CHAR_CHAR);
						proposal.setName(simpleTypeName);
						proposal.setRequiredProposals(new CompletionProposal[]{typeProposal});
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(flags);
						proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						proposal.setDeclarationTypeVariables(typeVariables);
						proposal.setCompatibleProposal(isCompatibleType);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				} else {
					this.noProposal = false;
					if(!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
						proposal.setDeclarationSignature(createNonGenericTypeSignature(packageName, typeName));
						proposal.setSignature(DEFAULT_CONSTRUCTOR_SIGNATURE);
						proposal.setDeclarationPackageName(packageName);
						proposal.setDeclarationTypeName(typeName);
						proposal.setParameterPackageNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterTypeNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterNames(CharOperation.NO_CHAR_CHAR);
						proposal.setName(simpleTypeName);
						proposal.setRequiredProposals(new CompletionProposal[]{typeProposal});
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(flags);
						proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						proposal.setDeclarationTypeVariables(typeVariables);
						proposal.setCompatibleProposal(isCompatibleType);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
				break;
			case 0: // constructor with no parameter

				if ((typeModifiers & ClassFileConstants.AccAbstract) != 0) {
					this.noProposal = false;
					if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
						InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
						proposal.setDeclarationSignature(createNonGenericTypeSignature(packageName, typeName));
						proposal.setDeclarationKey(createBindingKey(packageName, typeName));
						proposal.setSignature(DEFAULT_CONSTRUCTOR_SIGNATURE);
						proposal.setDeclarationPackageName(packageName);
						proposal.setDeclarationTypeName(typeName);
						proposal.setParameterPackageNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterTypeNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterNames(CharOperation.NO_CHAR_CHAR);
						proposal.setName(simpleTypeName);
						proposal.setRequiredProposals(new CompletionProposal[]{typeProposal});
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(modifiers);
						proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						proposal.setDeclarationTypeVariables(typeVariables);
						proposal.setCompatibleProposal(isCompatibleType);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				} else {
					this.noProposal = false;
					if(!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
						proposal.setDeclarationSignature(createNonGenericTypeSignature(packageName, typeName));
						proposal.setSignature(DEFAULT_CONSTRUCTOR_SIGNATURE);
						proposal.setDeclarationPackageName(packageName);
						proposal.setDeclarationTypeName(typeName);
						proposal.setParameterPackageNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterTypeNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterNames(CharOperation.NO_CHAR_CHAR);
						proposal.setName(simpleTypeName);
						proposal.setRequiredProposals(new CompletionProposal[]{typeProposal});
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(modifiers);
						proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						proposal.setDeclarationTypeVariables(typeVariables);
						proposal.setCompatibleProposal(isCompatibleType);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
				break;
			default: // constructor with parameter
				if (signature == null) {
					// resolve type to found parameter types
					signature = getResolvedSignature(parameterTypes, fullyQualifiedName, parameterCount, scope);
					if (signature == null) return;
				} else {
					signature = CharOperation.replaceOnCopy(signature, '/', '.');
				}

				int parameterNamesLength = parameterNames == null ? 0 : parameterNames.length;
				if (parameterCount != parameterNamesLength) {
					parameterNames = null;
				}

				if ((typeModifiers & ClassFileConstants.AccAbstract) != 0) {
					this.noProposal = false;
					if(!isIgnored(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
						InternalCompletionProposal proposal = createProposal(CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
						proposal.setDeclarationSignature(createNonGenericTypeSignature(packageName, typeName));
						proposal.setDeclarationKey(createBindingKey(packageName, typeName));
						proposal.setSignature(signature);
						proposal.setDeclarationPackageName(packageName);
						proposal.setDeclarationTypeName(typeName);
						proposal.setParameterPackageNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterTypeNames(CharOperation.NO_CHAR_CHAR);
						if (parameterNames != null) {
							proposal.setParameterNames(parameterNames);
						} else {
							proposal.setHasNoParameterNamesFromIndex(true);
						}
						proposal.setName(simpleTypeName);
						proposal.setRequiredProposals(new CompletionProposal[]{typeProposal});
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(modifiers);
						proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						proposal.setDeclarationTypeVariables(typeVariables);
						proposal.setCompatibleProposal(isCompatibleType);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				} else {
					this.noProposal = false;
					if(!isIgnored(CompletionProposal.CONSTRUCTOR_INVOCATION, CompletionProposal.TYPE_REF)) {
						InternalCompletionProposal proposal =  createProposal(CompletionProposal.CONSTRUCTOR_INVOCATION, this.actualCompletionPosition);
						proposal.setDeclarationSignature(createNonGenericTypeSignature(packageName, typeName));
						proposal.setSignature(signature);
						proposal.setDeclarationPackageName(packageName);
						proposal.setDeclarationTypeName(typeName);
						proposal.setParameterPackageNames(CharOperation.NO_CHAR_CHAR);
						proposal.setParameterTypeNames(CharOperation.NO_CHAR_CHAR);
						if (parameterNames != null) {
							proposal.setParameterNames(parameterNames);
						} else {
							proposal.setHasNoParameterNamesFromIndex(true);
						}
						proposal.setName(simpleTypeName);
						proposal.setRequiredProposals(new CompletionProposal[]{typeProposal});
						proposal.setIsContructor(true);
						proposal.setCompletion(completion);
						proposal.setFlags(modifiers);
						proposal.setReplaceRange(this.endPosition - this.offset, this.endPosition - this.offset);
						proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
						proposal.setRelevance(relevance);
						proposal.setDeclarationTypeVariables(typeVariables);
						proposal.setCompatibleProposal(isCompatibleType);
						this.requestor.accept(proposal);
						if(DEBUG) {
							this.printDebug(proposal);
						}
					}
				}
				break;
		}
	}

	private void proposeNewMethod(char[] token, ReferenceBinding reference) {
		if(!this.requestor.isIgnored(CompletionProposal.POTENTIAL_METHOD_DECLARATION)) {
			int relevance = computeBaseRelevance();
			relevance += computeRelevanceForResolution();
			relevance += computeRelevanceForInterestingProposal();
			relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE); // no access restriction for new method

			InternalCompletionProposal proposal =  createProposal(CompletionProposal.POTENTIAL_METHOD_DECLARATION, this.actualCompletionPosition);
			proposal.setDeclarationSignature(getSignature(reference));
			proposal.setSignature(
					createMethodSignature(
							CharOperation.NO_CHAR_CHAR,
							CharOperation.NO_CHAR_CHAR,
							CharOperation.NO_CHAR,
							VOID));
			proposal.setDeclarationPackageName(reference.qualifiedPackageName());
			proposal.setDeclarationTypeName(reference.qualifiedSourceName());

			//proposal.setPackageName(null);
			proposal.setTypeName(VOID);
			proposal.setName(token);
			//proposal.setParameterPackageNames(null);
			//proposal.setParameterTypeNames(null);
			//proposal.setPackageName(null);
			proposal.setCompletion(token);
			proposal.setFlags(Flags.AccPublic);
			proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
			proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
			proposal.setRelevance(relevance);
			this.requestor.accept(proposal);
			if(DEBUG) {
				this.printDebug(proposal);
			}
		}
	}

	private void proposeType(
			char[] packageName,
			char[] simpleTypeName,
			int modifiers,
			int accessibility,
			char[] typeName,
			char[] fullyQualifiedName,
			boolean isQualified,
			Scope scope) {
		char[] completionName = fullyQualifiedName;
		if(isQualified) {
			if (packageName == null || packageName.length == 0)
				if (this.unitScope != null && this.unitScope.fPackage.compoundName != CharOperation.NO_CHAR_CHAR)
					return; // ignore types from the default package from outside it
		} else {
			completionName = simpleTypeName;
		}

		if (this.requestor.isIgnored(fullyQualifiedName)) {
			return;
		}
		TypeBinding guessedType = null;
		if ((modifiers & ClassFileConstants.AccAnnotation) != 0 &&
				this.assistNodeIsAnnotation &&
				(this.targetedElement & TagBits.AnnotationTargetMASK) != 0) {
			char[][] cn = CharOperation.splitOn('.', fullyQualifiedName);

			TypeReference ref;
			if (cn.length == 1) {
				ref = new SingleTypeReference(simpleTypeName, 0);
			} else {
				ref = new QualifiedTypeReference(cn,new long[cn.length]);
			}

			switch (scope.kind) {
				case Scope.METHOD_SCOPE :
				case Scope.BLOCK_SCOPE :
					guessedType = ref.resolveType((BlockScope)scope);
					break;
				case Scope.CLASS_SCOPE :
					guessedType = ref.resolveType((ClassScope)scope);
					break;
			}

			if (guessedType == null || !guessedType.isValidBinding()) return;

			if (!hasPossibleAnnotationTarget(guessedType, scope)) return;
		}

		int relevance = computeBaseRelevance();
		relevance += computeRelevanceForResolution();
		relevance += computeRelevanceForInterestingProposal(packageName, fullyQualifiedName);
		relevance += computeRelevanceForRestrictions(accessibility);
		relevance += computeRelevanceForCaseMatching(this.completionToken, simpleTypeName);
		relevance += computeRelevanceForExpectingType(packageName, simpleTypeName);
		relevance += computeRelevanceForQualification(isQualified);

		int kind = modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccEnum | ClassFileConstants.AccAnnotation);
		switch (kind) {
			case ClassFileConstants.AccAnnotation:
			case ClassFileConstants.AccAnnotation | ClassFileConstants.AccInterface:
				relevance += computeRelevanceForAnnotation();
				if (guessedType != null) relevance += computeRelevanceForAnnotationTarget(guessedType);
				relevance += computeRelevanceForInterface();
				break;
			case ClassFileConstants.AccEnum:
				relevance += computeRelevanceForEnum();
				break;
			case ClassFileConstants.AccInterface:
				relevance += computeRelevanceForInterface();
				break;
			default:
				relevance += computeRelevanceForClass();
				relevance += computeRelevanceForException(simpleTypeName);
				break;
		}

		this.noProposal = false;
		if(!this.requestor.isIgnored(CompletionProposal.TYPE_REF)) {
			createTypeProposal(packageName, typeName, modifiers, accessibility, completionName, relevance);
		}
	}

	protected void reset() {

		super.reset(false);
		this.validPackageNames = new SimpleSetOfCharArray(10);
		this.invalidPackageNames = new SimpleSetOfCharArray(1);
		this.knownModules = new HashtableOfObject(10);
		this.knownPkgs = new HashtableOfObject(10);
		this.knownTypes = new HashtableOfObject(10);
		if (this.noCacheNameEnvironment != null) {
			this.noCacheNameEnvironment.cleanup();
			this.noCacheNameEnvironment = null;
			JavaModelManager.getJavaModelManager().flushZipFiles(this);
		}
	}

	private void setSourceAndTokenRange(int start, int end) {
		this.setSourceAndTokenRange(start, end, true);
	}

	private void setSourceAndTokenRange(int start, int end, boolean emptyTokenAdjstment) {
		this.setSourceRange(start, end, emptyTokenAdjstment);
		this.setTokenRange(start, end, emptyTokenAdjstment);
	}

	private void setSourceRange(int start, int end) {
		this.setSourceRange(start, end, true);
	}

	private void setSourceRange(int start, int end, boolean emptyTokenAdjstment) {
		this.startPosition = start;
		if(emptyTokenAdjstment) {
			int endOfEmptyToken = ((CompletionScanner)this.parser.scanner).endOfEmptyToken;
			this.endPosition = endOfEmptyToken > end ? endOfEmptyToken + 1 : end + 1;
		} else {
			this.endPosition = end + 1;
		}
	}

	private void setTokenRange(int start, int end) {
		this.setTokenRange(start, end, true);
	}
	private void setTokenRange(int start, int end, boolean emptyTokenAdjstment) {
		this.tokenStart = start;
		if(emptyTokenAdjstment) {
			int endOfEmptyToken = ((CompletionScanner)this.parser.scanner).endOfEmptyToken;
			this.tokenEnd = endOfEmptyToken > end ? endOfEmptyToken + 1 : end + 1;
		} else {
			this.tokenEnd = end + 1;
		}
	}

	private char[] substituteMethodTypeParameterName(char firstName, char startChar, char endChar, char[][] excludedNames, char[][] otherParameterNames) {
		char name = firstName;
		next : while (true) {
			for (int i = 0 ; i < excludedNames.length ; i++){
				if(excludedNames[i].length == 1 && ScannerHelper.toLowerCase(excludedNames[i][0]) == ScannerHelper.toLowerCase(name)) {
					name++;
					if(name > endChar)
						name = startChar;
					if(name == firstName)
						return substituteMethodTypeParameterName(new char[]{firstName}, excludedNames, otherParameterNames);
					continue next;
				}
			}

			for (int i = 0; i < otherParameterNames.length; i++) {
				if(otherParameterNames[i].length == 1 && ScannerHelper.toLowerCase(otherParameterNames[i][0]) == ScannerHelper.toLowerCase(name)) {
					name++;
					if(name > endChar)
						name = startChar;
					if(name == firstName)
						return substituteMethodTypeParameterName(new char[]{firstName}, excludedNames, otherParameterNames);
					continue next;
				}
			}
			break next;
		}
		return new char[]{name};
	}

	private char[] substituteMethodTypeParameterName(char[] firstName, char[][] excludedNames, char[][] otherParameterNames) {
		char[] name = firstName;
		int count = 2;
		next : while(true) {
			for(int k = 0 ; k < excludedNames.length ; k++){
				if(CharOperation.equals(name, excludedNames[k], false)) {
					name = CharOperation.concat(firstName, String.valueOf(count++).toCharArray());
					continue next;
				}
			}
			for (int i = 0; i < otherParameterNames.length; i++) {
				if(CharOperation.equals(name, otherParameterNames[i], false)) {
					name = CharOperation.concat(firstName, String.valueOf(count++).toCharArray());
					continue next;
				}
			}
			break next;
		}
		return name;
	}

	private char[][] substituteMethodTypeParameterNames(TypeVariableBinding[] typeVariables, char[][] excludedNames) {
		char[][] substituedParameterNames = new char[typeVariables.length][];

		for (int i = 0; i < substituedParameterNames.length; i++) {
			substituedParameterNames[i] = typeVariables[i].sourceName;
		}

		boolean foundConflicts = false;

		nextTypeParameter : for (int i = 0; i < typeVariables.length; i++) {
			TypeVariableBinding typeVariableBinding = typeVariables[i];
			char[] methodParameterName = typeVariableBinding.sourceName;

			for (int j = 0; j < excludedNames.length; j++) {
				char[] typeParameterName = excludedNames[j];
				if(CharOperation.equals(typeParameterName, methodParameterName, false)) {
					char[] substitution;
					if(methodParameterName.length == 1) {
						if(ScannerHelper.isUpperCase(methodParameterName[0])) {
							substitution = substituteMethodTypeParameterName(methodParameterName[0], 'A', 'Z', excludedNames, substituedParameterNames);
						} else {
							substitution = substituteMethodTypeParameterName(methodParameterName[0], 'a', 'z', excludedNames, substituedParameterNames);
						}
					} else {
						substitution = substituteMethodTypeParameterName(methodParameterName, excludedNames, substituedParameterNames);
					}
					substituedParameterNames[i] = substitution;

					foundConflicts = true;
					continue nextTypeParameter;
				}
			}
		}

		if(foundConflicts) return substituedParameterNames;
		return null;
	}

	private void findLambdaExpressions(Scope scope) {
		if (this.requestor.isIgnored(CompletionProposal.LAMBDA_EXPRESSION) ||
				this.compilerOptions.sourceLevel < ClassFileConstants.JDK1_8) {
			return;
		}

		if (this.expectedTypesPtr > -1) {
			for (int i = 0; i <= this.expectedTypesPtr; i++) {
				TypeBinding type = this.expectedTypes[i];

				if (type.isFunctionalInterface(scope)) {
					int relevance = computeBaseRelevance();
					relevance += R_EXACT_EXPECTED_TYPE;
					relevance += R_ABSTRACT_METHOD;
					relevance += computeRelevanceForResolution();
					relevance += computeRelevanceForInterestingProposal();
					relevance += computeRelevanceForRestrictions(IAccessRule.K_ACCESSIBLE);

					// we are sure this is not null since we already check for eligibility
					MethodBinding method = type.getSingleAbstractMethod(scope, true);
					int length = method.parameters.length;
					char[][] parameterTypeNames = new char[length][];

					for (int j = 0; j < length; j++) {
						TypeBinding p = method.parameters[j];
						parameterTypeNames[j] = p.qualifiedSourceName();
					}
					char[][] parameterNames = findMethodParameterNames(method, parameterTypeNames);

					InternalCompletionProposal proposal = createProposal(CompletionProposal.LAMBDA_EXPRESSION,
							this.actualCompletionPosition);
					proposal.setBinding(method);
					proposal.setDeclarationSignature(getSignature(method.declaringClass));
					proposal.setSignature(getSignature(method));
					MethodBinding original = method.original();
					if(original != method) {
						proposal.setOriginalSignature(getSignature(original));
					}
					proposal.setReplaceRange(this.startPosition - this.offset, this.endPosition - this.offset);
					proposal.setTokenRange(this.tokenStart - this.offset, this.tokenEnd - this.offset);
					proposal.setRelevance(relevance);
					proposal.setCompletion(LAMBDA);
					proposal.setParameterTypeNames(parameterTypeNames);
					proposal.setFlags(method.modifiers);
					proposal.setDeclarationPackageName(method.declaringClass.qualifiedPackageName());
					proposal.setDeclarationTypeName(method.declaringClass.qualifiedSourceName());
					proposal.setName(method.selector);
					proposal.setTypeName(method.returnType.qualifiedSourceName());
					if (parameterNames != null) {
						proposal.setParameterNames(parameterNames);
					}

					this.requestor.accept(proposal);
					if (DEBUG) {
						this.printDebug(proposal);
					}
				}
			}
		}
	}

	public void findPackagesInModule(char[] prefix,  ModuleBinding module, Scope scope) {
		if (module != null ) {
			String name= new String(prefix);
			String[] splittedName = Util.splitOn('.', new String(name), 0, name.length());
			PackageBinding[] pkgBindings = module.getExports();
			for (PackageBinding pkgBinding : pkgBindings) {
				String[] pkgName = CharOperation.toStrings(pkgBinding.compoundName);
				if (pkgName != null &&
						( name.isEmpty() ||
								Util.startsWithIgnoreCase(pkgName, splittedName, true))) {
					acceptPackage(CharOperation.toString(pkgBinding.compoundName).toCharArray(), module.moduleName);
				}
			}
		}
	}

}
