org.eclipse.jdt.internal.codeassist.CompletionEngine.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.codeassist.CompletionEngine.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2019 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)
 *     Gbor Kvesdn - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
 *******************************************************************************/
package org.eclipse.jdt.internal.codeassist;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;

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.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.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.CompletionOnBranchStatementLabel;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnClassLiteralAccess;
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.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.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.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.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.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.NameReference;
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.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.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.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.IndexBasedJavaSearchEnvironment;
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() {
            StringBuffer buffer = new StringBuffer();
            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() {
            StringBuffer buffer = new StringBuffer();
            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;
        StringBuffer sig = new StringBuffer(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[] JAVA_LANG_OBJECT_SIGNATURE = createTypeSignature(
            CharOperation.concatWith(JAVA_LANG, '.'), OBJECT);
    private final static char[] JAVA_LANG_NAME = CharOperation.concatWith(JAVA_LANG, '.');

    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 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 (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);
            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) {

        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);
            proposal.setDeclarationSignature(packageName);
            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.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 = CharOperation.concat(completionName, new char[] { ';' });
                        }
                    } else {
                        completionName = CharOperation.concat(completionName, new char[] { ';' });
                    }

                    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
        }
    }

    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)
            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) {
            System.out.println(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 (astNode instanceof CompletionOnQualifiedTypeReference) {
            CompletionOnQualifiedTypeReference completionOnQualifiedTypeReference = (CompletionOnQualifiedTypeReference) astNode;
            if (completionOnQualifiedTypeReference.isConstructorType) {
                context.setTokenLocation(CompletionContext.TL_CONSTRUCTOR_START);
            }
        } else if (astNode instanceof CompletionOnKeyword3 && ((CompletionOnKeyword3) astNode).afterTryOrCatch()) {
            context.setTokenLocation(CompletionContext.TL_STATEMENT_START);
        } 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)
                            && 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, qualifiedBinding, scope);
        } else if (astNode instanceof CompletionOnMemberAccess) {
            completionOnMemberAccess(astNode, enclosingNode, qualifiedBinding, scope, insideTypeAnnotation);
        } else if (astNode instanceof CompletionOnMessageSend) {
            completionOnMessageSend(astNode, qualifiedBinding, scope);
        } else if (astNode instanceof CompletionOnExplicitConstructorCall) {
            completionOnExplicitConstructorCall(astNode, qualifiedBinding, scope);
        } else if (astNode instanceof CompletionOnQualifiedAllocationExpression) {
            completionOnQualifiedAllocationExpression(astNode, 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 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 CompletionOnBranchStatementLabel) {
            completionOnBranchStatementLabel(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 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) {
            System.out.print("COMPLETION IN "); //$NON-NLS-1$
            System.out.print(sourceUnit.getFileName());
            System.out.print(" AT POSITION "); //$NON-NLS-1$
            System.out.println(completionPosition);
            System.out.println("COMPLETION - Source :"); //$NON-NLS-1$
            System.out.println(sourceUnit.getContents());
        }
        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.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) {
                    System.out.println("COMPLETION - Diet AST :"); //$NON-NLS-1$
                    System.out.println(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];
                                this.parser.enclosingNode = usesStatement;
                                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) {
                                System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$
                                System.out.println(e.astNode.toString());
                                if (this.parser.assistNodeParent != null) {
                                    System.out.print("COMPLETION - Parent Node : "); //$NON-NLS-1$
                                    System.out.println(this.parser.assistNodeParent);
                                }
                            }
                            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, null);

                                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.source = sourceUnit.getContents();
                            this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
                            parsedUnit.scope.faultInTypes();
                            parseBlockStatements(parsedUnit, this.actualCompletionPosition);
                            if (DEBUG) {
                                System.out.println("COMPLETION - AST :"); //$NON-NLS-1$
                                System.out.println(parsedUnit.toString());
                            }
                            parsedUnit.resolve();
                        }
                    } catch (CompletionNodeFound e) {
                        //               completionNodeFound = true;
                        if (e.astNode != null) {
                            // if null then we found a problem in the completion node
                            if (DEBUG) {
                                System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$
                                System.out.println(e.astNode.toString());
                                if (this.parser.assistNodeParent != null) {
                                    System.out.print("COMPLETION - Parent Node : "); //$NON-NLS-1$
                                    System.out.println(this.parser.assistNodeParent);
                                }
                            }
                            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) {
                System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
                e.printStackTrace(System.out);
            }
        } 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) {
                        System.out.print("COMPLETION - Completion node : "); //$NON-NLS-1$
                        System.out.println(e.astNode.toString());
                        if (this.parser.assistNodeParent != null) {
                            System.out.print("COMPLETION - Parent Node : "); //$NON-NLS-1$
                            System.out.println(this.parser.assistNodeParent);
                        }
                    }
                    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) {
                    System.out.println("SNIPPET COMPLETION AST :"); //$NON-NLS-1$
                    System.out.println(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) {
                System.out.println("Exception caught by CompletionEngine:"); //$NON-NLS-1$
                e.printStackTrace(System.out);
            }
        } 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 completionOnBranchStatementLabel(ASTNode astNode) {
        if (!this.requestor.isIgnored(CompletionProposal.LABEL_REF)) {
            CompletionOnBranchStatementLabel label = (CompletionOnBranchStatementLabel) astNode;
            this.completionToken = label.label;
            findLabels(this.completionToken, label.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 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);
        }
    }

    //TODO
    private void completionOnJavadocQualifiedTypeReference(ASTNode astNode, Binding qualifiedBinding, Scope scope) {
        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);
        }
    }

    //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());
    }

    //TODO
    private void completionOnJavadocTag(ASTNode astNode) {
        CompletionOnJavadocTag javadocTag = (CompletionOnJavadocTag) astNode;
        setSourceAndTokenRange(javadocTag.tagSourceStart, javadocTag.sourceEnd);
        findJavadocBlockTags(javadocTag);
        findJavadocInlineTags(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;
                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;
        if (qualifiedBinding == null) {
            if (!this.requestor.isIgnored(CompletionProposal.METHOD_REF)) {
                ObjectVector methodsFound = new ObjectVector();

                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, new ObjectVector(), false, true, messageSend, scope, false,
                    messageSend.receiver instanceof SuperReference, false, null, null, null, false, null, -1, -1);
        }
    }

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

            this.insideQualifiedReference = true;
            this.completionToken = messageSend.selector;
            boolean onlyStatic = false;
            if (messageSend.receiver instanceof NameReference) {
                onlyStatic = ((NameReference) messageSend.receiver).isTypeReference();
            } else if (!(messageSend.receiver instanceof MessageSend)
                    && !(messageSend.receiver instanceof FieldReference) && !(messageSend.receiver.isThis())) {
                onlyStatic = 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(), onlyStatic, 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);
            }
        }
    }

    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;

        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);
            }

            checkCancel();

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

    private void completionOnQualifiedNameReference(ASTNode astNode, ASTNode enclosingNode,
            Binding qualifiedBinding, Scope scope, boolean insideTypeAnnotation) {
        this.insideQualifiedReference = true;
        CompletionOnQualifiedNameReference ref = (CompletionOnQualifiedNameReference) astNode;
        this.completionToken = ref.completionIdentifier;
        long completionPosition = ref.sourcePositions[ref.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 (ref.tokens.length == 1) {
                    boolean foundSomeFields = findFieldsAndMethodsFromMissingFieldType(ref.tokens[0], scope, ref,
                            insideTypeAnnotation);

                    if (!foundSomeFields) {

                        checkCancel();

                        findMembersFromMissingType(ref.tokens[0], ref.sourcePositions[0], null, scope, ref,
                                ref.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, ref, scope, false, false, null, null, null, false, null, -1, -1);

                checkCancel();

                findFieldsAndMethodsFromCastedReceiver(enclosingNode, qualifiedBinding, scope, fieldsFound,
                        methodsFound, ref, scope, 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 (ref.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, ref, scope);
                        } else {
                            // complete field members with missing fields type
                            // class X {
                            //   Missing f;
                            //   void foo() {
                            //     f.|
                            //   }
                            // }
                            findFieldsAndMethodsFromMissingFieldType(ref.tokens[0], scope, ref,
                                    insideTypeAnnotation);
                        }

                    }
                }
            }

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

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

        } 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);
        }
    }

    private void completionOnQualifiedTypeReference(ASTNode astNode, ASTNode astNodeParent,
            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];

        // 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)) {
            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, (ReferenceBinding) qualifiedBinding,
                            scope.enclosingSourceType(), (BlockScope) scope, typesFound);
                }

                checkCancel();

                findMemberTypes(this.completionToken, (ReferenceBinding) qualifiedBinding, scope,
                        scope.enclosingSourceType(), false, false, typesFound, null, null, null, false);
            }
        } 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);
        }
    }

    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, 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;
        if (switchStatement != null && switchStatement.expression.resolvedType != null
                && switchStatement.expression.resolvedType.isEnum()) {
            if (!this.requestor.isIgnored(CompletionProposal.FIELD_REF)) {
                this.assistNodeIsEnum = true;
                findEnumConstantsFromSwithStatement(this.completionToken, (SwitchStatement) astNodeParent);
            }
        } 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);

                findUnresolvedReference(singleNameReference.sourceStart, singleNameReference.sourceEnd,
                        (BlockScope) scope, alreadyDefinedName);
            }

            checkCancel();

            findVariablesAndMethods(this.completionToken, scope, singleNameReference, scope, insideTypeAnnotation,
                    singleNameReference.isInsideAnnotationAttribute);

            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 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);
                }

                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);
        }
    }

    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 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.type.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);
                        }
                    }
                }
            }
        }

        if (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) {

        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) {

        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;

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

            TypeBinding expectedType = method.parameters[arguments.length - 1];
            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) {

        StringBuffer completion = new StringBuffer(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[] 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;
        }
        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.isCompatibleWith(this.expectedTypes[i])) {

                    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;
                }
                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 null;
        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));
            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));
            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) {

        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);
    }

    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) {

        // 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]))
                                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 != 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);
            relevance += computeRelevanceForExpectingType(field.type);
            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 caseExpression = cases[i].constantExpression;
                if ((caseExpression instanceof SingleNameReference)
                        && (caseExpression.resolvedType != null && caseExpression.resolvedType.isEnum())) {
                    alreadyUsedConstants[alreadyUsedConstantCount++] = ((SingleNameReference) cases[i].constantExpression).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;
                    }
                }
            }
        }
        // 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);
            relevance += computeRelevanceForExpectingType(field.type);
            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 != 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 || !(enclosingNode instanceof IfStatement))
            return;

        IfStatement ifStatement = (IfStatement) enclosingNode;
        while (true) {
            if (!(ifStatement.condition instanceof InstanceOfExpression))
                return;

            InstanceOfExpression instanceOfExpression = (InstanceOfExpression) ifStatement.condition;

            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
            if (ifStatement.thenStatement instanceof IfStatement) {
                ifStatement = (IfStatement) ifStatement.thenStatement;
            } else {
                break;
            }
        }
    }

    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, this.options.camelCaseMatch,
                    IJavaSearchConstants.TYPE, 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);
                }
            }
        }
    }

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

        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);
            relevance += computeRelevanceForExpectingType(method.returnType);
            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 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);
            relevance += computeRelevanceForExpectingType(method.returnType);
            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);
            relevance += computeRelevanceForExpectingType(method.returnType);
            relevance += computeRelevanceForEnumConstant(method.returnType);
            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) {
                    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 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 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 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));

        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);
            }
        }

        boolean isEmptyPrefix = token.length == 0;

        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.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 (proposeConstructor) {

            checkCancel();

            findTypesFromExpectedTypes(token, scope, typesFound, proposeType, proposeConstructor);
        }

        if (isEmptyPrefix && !this.assistNodeIsAnnotation) {
            if (!proposeConstructor) {
                findTypesFromExpectedTypes(token, scope, typesFound, proposeType, proposeConstructor);
            }
        } 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, this.options.camelCaseMatch, 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, this.options.camelCaseMatch, searchFor,
                        this, this.monitor);
                acceptTypes(scope);
            }
            if (!isEmptyPrefix && !this.requestor.isIgnored(CompletionProposal.PACKAGE_REF)) {

                checkCancel();

                this.nameEnvironment.findPackages(token, this);
            }
        }
    }

    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, this.options.camelCaseMatch, 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, this.options.camelCaseMatch, searchFor, 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();

                if (this.expectedTypes[i] instanceof ReferenceBinding) {
                    ReferenceBinding refBinding = (ReferenceBinding) this.expectedTypes[i];

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

                    typesFound.add(refBinding);

                    boolean inSameUnit = this.unitScope.isDefinedInSameUnit(refBinding);

                    // top level types of the current unit are already proposed.
                    if (!inSameUnit || (inSameUnit && refBinding.isMemberType())) {
                        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);
                        relevance += computeRelevanceForExpectingType(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 || !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);
                                this.requestor.accept(proposal);
                                if (DEBUG) {
                                    this.printDebug(proposal);
                                }
                            }
                        }

                        if (proposeConstructor) {
                            findConstructorsOrAnonymousTypes(refBinding, scope, FakeInvocationSite, isQualified,
                                    relevance);
                        }
                    }
                }
            }
        }
    }

    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) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        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) {

        if (token == null)
            return;

        // Should local variables hide fields from the receiver type or any of its enclosing types?
        // we know its an implicit field/method access... see BlockScope getBinding/getImplicitMethod

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

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

        Scope currentScope = scope;

        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);
                        relevance += computeRelevanceForExpectingType(local.type);
                        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, true, null, null, null, false, null, -1,
                                    -1);
                        }
                        if (proposeMethod && !insideAnnotationAttribute) {
                            findMethods(token, null, null, enclosingType, classScope, methodsFound, staticsOnly,
                                    false, invocationSite, invocationScope, true, false, true, 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);
        }
    }

    private char[] getCompletedTypeSignature(ReferenceBinding referenceBinding) {
        char[] result = null;
        StringBuffer sig = new StringBuffer(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);
        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 = IndexBasedJavaSearchEnvironment.create(
                    Collections.singletonList(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.
     * @param completionName
     * @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)) {
            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;
            if (ref.resolvedType == null) {
                return false;
            }
            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;
                    }
                }
            }
        }
        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) {
            System.out.print("COMPLETION - completionFailure("); //$NON-NLS-1$
            System.out.print(error);
            System.out.println(")"); //$NON-NLS-1$
        }
    }

    protected void printDebug(CompletionProposal proposal) {
        StringBuffer buffer = new StringBuffer();
        printDebug(proposal, 0, buffer);
        System.out.println(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;
        default:
            buffer.append("PROPOSAL"); //$NON-NLS-1$
            break;

        }

        buffer.append("{\n");//$NON-NLS-1$
        printDebugTab(tab, buffer);
        buffer.append("\tCompletion[") //$NON-NLS-1$
                .append(proposal.getCompletion() == null ? "null".toCharArray() : proposal.getCompletion()) //$NON-NLS-1$
                .append("]\n"); //$NON-NLS-1$
        printDebugTab(tab, buffer);
        buffer.append("\tDeclarationSignature[") //$NON-NLS-1$
                .append(proposal.getDeclarationSignature() == null ? "null".toCharArray() //$NON-NLS-1$
                        : proposal.getDeclarationSignature())
                .append("]\n"); //$NON-NLS-1$
        printDebugTab(tab, buffer);
        buffer.append("\tDeclarationKey[") //$NON-NLS-1$
                .append(proposal.getDeclarationKey() == null ? "null".toCharArray() : proposal.getDeclarationKey()) //$NON-NLS-1$
                .append("]\n"); //$NON-NLS-1$
        printDebugTab(tab, buffer);
        buffer.append("\tSignature[") //$NON-NLS-1$
                .append(proposal.getSignature() == null ? "null".toCharArray() : proposal.getSignature()) //$NON-NLS-1$
                .append("]\n"); //$NON-NLS-1$
        printDebugTab(tab, buffer);
        buffer.append("\tKey[").append(proposal.getKey() == null ? "null".toCharArray() : proposal.getKey()) //$NON-NLS-1$//$NON-NLS-2$
                .append("]\n"); //$NON-NLS-1$
        printDebugTab(tab, buffer);
        buffer.append("\tName[").append(proposal.getName() == null ? "null".toCharArray() : proposal.getName()) //$NON-NLS-1$//$NON-NLS-2$
                .append("]\n"); //$NON-NLS-1$

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

        int relevance = computeBaseRelevance();
        relevance += computeRelevanceForResolution();
        relevance += computeRelevanceForInterestingProposal();
        relevance += computeRelevanceForRestrictions(accessibility);
        relevance += computeRelevanceForCaseMatching(this.completionToken, simpleTypeName);
        relevance += computeRelevanceForExpectingType(packageName, simpleTypeName);
        relevance += computeRelevanceForQualification(isQualified);
        relevance += computeRelevanceForConstructor();

        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);

        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);
                    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);
                    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);
                    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);
                    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);
                    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);

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

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