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

Java tutorial

Introduction

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

Source

/*******************************************************************************
 * Copyright (c) 2000, 2018 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:
 *     IBM Corporation - initial API and implementation
 *     Jesper Steen Mller <jesper@selskabet.org> - contributions for:   
 *         Bug 531046: [10] ICodeAssist#codeSelect support for 'var'
 *******************************************************************************/
package org.eclipse.jdt.internal.codeassist;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.IOrdinaryClassFile;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.TypeNameMatch;
import org.eclipse.jdt.core.search.TypeNameMatchRequestor;
import org.eclipse.jdt.internal.codeassist.impl.AssistParser;
import org.eclipse.jdt.internal.codeassist.impl.Engine;
import org.eclipse.jdt.internal.codeassist.select.SelectionJavadocParser;
import org.eclipse.jdt.internal.codeassist.select.SelectionNodeFound;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnPackageVisibilityReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnImportReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnPackageReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionParser;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ModuleDeclaration;
import org.eclipse.jdt.internal.compiler.ast.PackageVisibilityStatement;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
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.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.IntersectionTypeBinding18;
import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MemberTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.ModuleBinding;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ProblemFieldBinding;
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.SyntheticMethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
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.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.core.BinaryTypeConverter;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.JrtPackageFragmentRoot;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SelectionRequestor;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.TypeNameMatchRequestorWrapper;
import org.eclipse.jdt.internal.core.util.ASTNodeFinder;
import org.eclipse.jdt.internal.core.util.HashSetOfCharArrayArray;

/**
 * The selection engine is intended to infer the nature of a selected name in some
 * source code. This name can be qualified.
 *
 * Selection is resolving context using a name environment (no need to search), assuming
 * the source where selection occurred is correct and will not perform any completion
 * attempt. If this was the desired behavior, a call to the CompletionEngine should be
 * performed instead.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public final class SelectionEngine extends Engine implements ISearchRequestor {

    private static class SelectionTypeNameMatchRequestorWrapper extends TypeNameMatchRequestorWrapper {

        class AcceptedType {
            public int modifiers;
            public char[] packageName;
            public char[] simpleTypeName;
            public String path;
            public AccessRestriction access;

            public AcceptedType(int modifiers, char[] packageName, char[] simpleTypeName, String path,
                    AccessRestriction access) {
                this.modifiers = modifiers;
                this.packageName = packageName;
                this.simpleTypeName = simpleTypeName;
                this.path = path;
                this.access = access;
            }
        }

        private ImportReference[] importReferences;

        private boolean importCachesNodeInitialized = false;
        private ImportReference[] onDemandImportsNodeCache;
        private int onDemandImportsNodeCacheCount;
        private char[][][] importsNodeCache;
        private int importsNodeCacheCount;

        private HashtableOfObject onDemandFound = new HashtableOfObject();
        private ObjectVector notImportedFound = new ObjectVector();

        public SelectionTypeNameMatchRequestorWrapper(TypeNameMatchRequestor requestor, IJavaSearchScope scope,
                ImportReference[] importReferences) {
            super(requestor, scope);
            this.importReferences = importReferences;
        }

        @Override
        public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName,
                char[][] enclosingTypeNames, String path, AccessRestriction access) {
            if (enclosingTypeNames != null && enclosingTypeNames.length > 0)
                return;

            if (!this.importCachesNodeInitialized)
                initializeImportNodeCaches();

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

            for (int i = 0; i < this.importsNodeCacheCount; i++) {
                char[][] importName = this.importsNodeCache[i];
                if (CharOperation.equals(importName[0], simpleTypeName)) {

                    if (CharOperation.equals(importName[1], fullyQualifiedTypeName)) {
                        super.acceptType(modifiers, packageName, simpleTypeName, enclosingTypeNames, path, access);
                    }
                    return;
                }
            }

            for (int i = 0; i < this.onDemandImportsNodeCacheCount; i++) {
                char[][] importName = this.onDemandImportsNodeCache[i].tokens;
                char[] importFlatName = CharOperation.concatWith(importName, '.');

                if (CharOperation.equals(importFlatName, packageName)) {

                    this.onDemandFound.put(simpleTypeName, simpleTypeName);
                    super.acceptType(modifiers, packageName, simpleTypeName, enclosingTypeNames, path, access);
                    return;
                }
            }

            this.notImportedFound.add(new AcceptedType(modifiers, packageName, simpleTypeName, path, access));
        }

        public void acceptNotImported() {
            int size = this.notImportedFound.size();
            for (int i = 0; i < size; i++) {
                AcceptedType acceptedType = (AcceptedType) this.notImportedFound.elementAt(i);

                if (this.onDemandFound.get(acceptedType.simpleTypeName) == null) {
                    super.acceptType(acceptedType.modifiers, acceptedType.packageName, acceptedType.simpleTypeName,
                            null, acceptedType.path, acceptedType.access);
                }
            }
        }

        public void initializeImportNodeCaches() {
            int length = this.importReferences == null ? 0 : this.importReferences.length;

            for (int i = 0; i < length; i++) {
                ImportReference importReference = this.importReferences[i];
                if ((importReference.bits & ASTNode.OnDemand) != 0) {
                    if (this.onDemandImportsNodeCache == null) {
                        this.onDemandImportsNodeCache = new ImportReference[length - i];
                    }
                    this.onDemandImportsNodeCache[this.onDemandImportsNodeCacheCount++] = importReference;
                } else {
                    if (this.importsNodeCache == null) {
                        this.importsNodeCache = new char[length - i][][];
                    }

                    this.importsNodeCache[this.importsNodeCacheCount++] = new char[][] {
                            importReference.tokens[importReference.tokens.length - 1],
                            CharOperation.concatWith(importReference.tokens, '.') };
                }
            }

            this.importCachesNodeInitialized = true;
        }
    }

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

    SelectionParser parser;
    ISelectionRequestor requestor;
    WorkingCopyOwner owner;

    boolean acceptedAnswer;

    private int actualSelectionStart;
    private int actualSelectionEnd;
    private char[] selectedIdentifier;

    private char[][][] acceptedClasses;
    private int[] acceptedClassesModifiers;
    private char[][][] acceptedInterfaces;
    private int[] acceptedInterfacesModifiers;
    private char[][][] acceptedEnums;
    private int[] acceptedEnumsModifiers;
    private char[][][] acceptedAnnotations;
    private int[] acceptedAnnotationsModifiers;
    int acceptedClassesCount;
    int acceptedInterfacesCount;
    int acceptedEnumsCount;
    int acceptedAnnotationsCount;

    boolean noProposal = true;
    CategorizedProblem problem = null;

    /**
     * The SelectionEngine is responsible for computing the selected object.
     *
     * 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.core.SearchableEnvironment
     *      used to resolve type/package references and search for types/packages
     *      based on partial names.
     *
     *  @param requestor org.eclipse.jdt.internal.codeassist.ISelectionRequestor
     *      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 SelectionEngine(SearchableEnvironment nameEnvironment, ISelectionRequestor requestor, Map settings,
            WorkingCopyOwner owner) {

        super(settings);

        this.requestor = requestor;
        this.nameEnvironment = nameEnvironment;

        ProblemReporter problemReporter = new ProblemReporter(DefaultErrorHandlingPolicies.proceedWithAllProblems(),
                this.compilerOptions, new DefaultProblemFactory(Locale.getDefault())) {

            @Override
            public CategorizedProblem createProblem(char[] fileName, int problemId, String[] problemArguments,
                    String[] messageArguments, int severity, int problemStartPosition, int problemEndPosition,
                    int lineNumber, int columnNumber) {
                CategorizedProblem pb = super.createProblem(fileName, problemId, problemArguments, messageArguments,
                        severity, problemStartPosition, problemEndPosition, lineNumber, columnNumber);
                if (SelectionEngine.this.problem == null && pb.isError() && (pb.getID() & IProblem.Syntax) == 0) {
                    SelectionEngine.this.problem = pb;
                }

                return pb;
            }
        };
        this.lookupEnvironment = new LookupEnvironment(this, this.compilerOptions, problemReporter,
                nameEnvironment);
        this.parser = new SelectionParser(problemReporter);
        this.owner = owner;
    }

    @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 access) {
        // constructors aren't searched
    }

    @Override
    public void acceptType(char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, int modifiers,
            AccessRestriction accessRestriction) {
        char[] typeName = enclosingTypeNames == null ? simpleTypeName
                : CharOperation.concat(CharOperation.concatWith(enclosingTypeNames, '.'), simpleTypeName, '.');

        if (CharOperation.equals(simpleTypeName, this.selectedIdentifier)) {
            char[] flatEnclosingTypeNames = enclosingTypeNames == null || enclosingTypeNames.length == 0 ? null
                    : CharOperation.concatWith(enclosingTypeNames, '.');
            if (mustQualifyType(packageName, simpleTypeName, flatEnclosingTypeNames, modifiers)) {
                int length = 0;
                int kind = modifiers & (ClassFileConstants.AccInterface | ClassFileConstants.AccEnum
                        | ClassFileConstants.AccAnnotation);
                switch (kind) {
                case ClassFileConstants.AccAnnotation:
                case ClassFileConstants.AccAnnotation | ClassFileConstants.AccInterface:
                    char[][] acceptedAnnotation = new char[2][];
                    acceptedAnnotation[0] = packageName;
                    acceptedAnnotation[1] = typeName;

                    if (this.acceptedAnnotations == null) {
                        this.acceptedAnnotations = new char[10][][];
                        this.acceptedAnnotationsModifiers = new int[10];
                        this.acceptedAnnotationsCount = 0;
                    }
                    length = this.acceptedAnnotations.length;
                    if (length == this.acceptedAnnotationsCount) {
                        int newLength = (length + 1) * 2;
                        System.arraycopy(this.acceptedAnnotations, 0,
                                this.acceptedAnnotations = new char[newLength][][], 0, length);
                        System.arraycopy(this.acceptedAnnotationsModifiers, 0,
                                this.acceptedAnnotationsModifiers = new int[newLength], 0, length);
                    }
                    this.acceptedAnnotationsModifiers[this.acceptedAnnotationsCount] = modifiers;
                    this.acceptedAnnotations[this.acceptedAnnotationsCount++] = acceptedAnnotation;
                    break;
                case ClassFileConstants.AccEnum:
                    char[][] acceptedEnum = new char[2][];
                    acceptedEnum[0] = packageName;
                    acceptedEnum[1] = typeName;

                    if (this.acceptedEnums == null) {
                        this.acceptedEnums = new char[10][][];
                        this.acceptedEnumsModifiers = new int[10];
                        this.acceptedEnumsCount = 0;
                    }
                    length = this.acceptedEnums.length;
                    if (length == this.acceptedEnumsCount) {
                        int newLength = (length + 1) * 2;
                        System.arraycopy(this.acceptedEnums, 0, this.acceptedEnums = new char[newLength][][], 0,
                                length);
                        System.arraycopy(this.acceptedEnumsModifiers, 0,
                                this.acceptedEnumsModifiers = new int[newLength], 0, length);
                    }
                    this.acceptedEnumsModifiers[this.acceptedEnumsCount] = modifiers;
                    this.acceptedEnums[this.acceptedEnumsCount++] = acceptedEnum;
                    break;
                case ClassFileConstants.AccInterface:
                    char[][] acceptedInterface = new char[2][];
                    acceptedInterface[0] = packageName;
                    acceptedInterface[1] = typeName;

                    if (this.acceptedInterfaces == null) {
                        this.acceptedInterfaces = new char[10][][];
                        this.acceptedInterfacesModifiers = new int[10];
                        this.acceptedInterfacesCount = 0;
                    }
                    length = this.acceptedInterfaces.length;
                    if (length == this.acceptedInterfacesCount) {
                        int newLength = (length + 1) * 2;
                        System.arraycopy(this.acceptedInterfaces, 0,
                                this.acceptedInterfaces = new char[newLength][][], 0, length);
                        System.arraycopy(this.acceptedInterfacesModifiers, 0,
                                this.acceptedInterfacesModifiers = new int[newLength], 0, length);
                    }
                    this.acceptedInterfacesModifiers[this.acceptedInterfacesCount] = modifiers;
                    this.acceptedInterfaces[this.acceptedInterfacesCount++] = acceptedInterface;
                    break;
                default:
                    char[][] acceptedClass = new char[2][];
                    acceptedClass[0] = packageName;
                    acceptedClass[1] = typeName;

                    if (this.acceptedClasses == null) {
                        this.acceptedClasses = new char[10][][];
                        this.acceptedClassesModifiers = new int[10];
                        this.acceptedClassesCount = 0;
                    }
                    length = this.acceptedClasses.length;
                    if (length == this.acceptedClassesCount) {
                        int newLength = (length + 1) * 2;
                        System.arraycopy(this.acceptedClasses, 0, this.acceptedClasses = new char[newLength][][], 0,
                                length);
                        System.arraycopy(this.acceptedClassesModifiers, 0,
                                this.acceptedClassesModifiers = new int[newLength], 0, length);
                    }
                    this.acceptedClassesModifiers[this.acceptedClassesCount] = modifiers;
                    this.acceptedClasses[this.acceptedClassesCount++] = acceptedClass;
                    break;
                }
            } else {
                this.noProposal = false;
                this.requestor.acceptType(packageName, typeName, modifiers, false, null, this.actualSelectionStart,
                        this.actualSelectionEnd);
                this.acceptedAnswer = true;
            }
        }
    }

    @Override
    public void acceptPackage(char[] packageName) {
        // implementation of interface method
    }

    private void acceptQualifiedTypes() {
        if (this.acceptedClasses != null) {
            this.acceptedAnswer = true;
            for (int i = 0; i < this.acceptedClassesCount; i++) {
                this.noProposal = false;
                this.requestor.acceptType(this.acceptedClasses[i][0], this.acceptedClasses[i][1],
                        this.acceptedClassesModifiers[i], false, null, this.actualSelectionStart,
                        this.actualSelectionEnd);
            }
            this.acceptedClasses = null;
            this.acceptedClassesModifiers = null;
            this.acceptedClassesCount = 0;
        }
        if (this.acceptedInterfaces != null) {
            this.acceptedAnswer = true;
            for (int i = 0; i < this.acceptedInterfacesCount; i++) {
                this.noProposal = false;
                this.requestor.acceptType(this.acceptedInterfaces[i][0], this.acceptedInterfaces[i][1],
                        this.acceptedInterfacesModifiers[i], false, null, this.actualSelectionStart,
                        this.actualSelectionEnd);
            }
            this.acceptedInterfaces = null;
            this.acceptedInterfacesModifiers = null;
            this.acceptedInterfacesCount = 0;
        }
        if (this.acceptedAnnotations != null) {
            this.acceptedAnswer = true;
            for (int i = 0; i < this.acceptedAnnotationsCount; i++) {
                this.noProposal = false;
                this.requestor.acceptType(this.acceptedAnnotations[i][0], this.acceptedAnnotations[i][1],
                        this.acceptedAnnotationsModifiers[i], false, null, this.actualSelectionStart,
                        this.actualSelectionEnd);
            }
            this.acceptedAnnotations = null;
            this.acceptedAnnotationsModifiers = null;
            this.acceptedAnnotationsCount = 0;
        }
        if (this.acceptedEnums != null) {
            this.acceptedAnswer = true;
            for (int i = 0; i < this.acceptedEnumsCount; i++) {
                this.noProposal = false;
                this.requestor.acceptType(this.acceptedEnums[i][0], this.acceptedEnums[i][1],
                        this.acceptedEnumsModifiers[i], false, null, this.actualSelectionStart,
                        this.actualSelectionEnd);
            }
            this.acceptedEnums = null;
            this.acceptedEnumsModifiers = null;
            this.acceptedEnumsCount = 0;
        }
    }

    private boolean checkSelection(char[] source, int selectionStart, int selectionEnd, boolean isModuleInfo) {

        Scanner scanner = new Scanner(false /*comment*/, false /*whitespace*/, false /*nls*/,
                this.compilerOptions.sourceLevel, this.compilerOptions.complianceLevel, null/*taskTag*/,
                null/*taskPriorities*/, true /*taskCaseSensitive*/, this.compilerOptions.enablePreviewFeatures);
        scanner.setSource(source);

        int lastIdentifierStart = -1;
        int lastIdentifierEnd = -1;
        char[] lastIdentifier = null;
        int token;

        if (selectionStart > selectionEnd) {
            int end = source.length - 1;

            // compute start position of current line
            int currentPosition = selectionStart - 1;
            int nextCharacterPosition = selectionStart;
            char currentCharacter = ' ';
            try {
                lineLoop: while (currentPosition > 0) {

                    if (source[currentPosition] == '\\' && source[currentPosition + 1] == 'u') {
                        int pos = currentPosition + 2;
                        int c1 = 0, c2 = 0, c3 = 0, c4 = 0;
                        while (source[pos] == 'u') {
                            pos++;
                        }

                        int endOfUnicode = pos + 3;
                        if (end < endOfUnicode) {
                            if (endOfUnicode < source.length) {
                                end = endOfUnicode;
                            } else {
                                return false; // not enough characters to decode an unicode
                            }
                        }

                        if ((c1 = ScannerHelper.getHexadecimalValue(source[pos++])) > 15 || c1 < 0
                                || (c2 = ScannerHelper.getHexadecimalValue(source[pos++])) > 15 || c2 < 0
                                || (c3 = ScannerHelper.getHexadecimalValue(source[pos++])) > 15 || c3 < 0
                                || (c4 = ScannerHelper.getHexadecimalValue(source[pos++])) > 15 || c4 < 0) {
                            return false;
                        } else {
                            currentCharacter = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
                            nextCharacterPosition = pos;
                        }
                    } else {
                        currentCharacter = source[currentPosition];
                        nextCharacterPosition = currentPosition + 1;
                    }

                    switch (currentCharacter) {
                    case '\r':
                    case '\n':
                    case '/':
                    case '"':
                    case '\'':
                        break lineLoop;
                    case '-':
                        if (source[nextCharacterPosition] == '>') {
                            nextCharacterPosition--; // nextCharacterPosition = currentPosition
                            break lineLoop;
                        }
                        break;
                    case ':':
                        if (source[nextCharacterPosition] == ':') {
                            nextCharacterPosition--; // nextCharacterPosition = currentPosition
                            break lineLoop;
                        }
                    }
                    currentPosition--;
                }
            } catch (ArrayIndexOutOfBoundsException e) {
                return false;
            }

            // compute start and end of the last token
            scanner.resetTo(nextCharacterPosition, end, isModuleInfo);
            isolateLastName: do {
                try {
                    token = scanner.getNextToken();
                } catch (InvalidInputException e) {
                    return false;
                }
                switch (token) {
                case TerminalTokens.TokenNamethis:
                case TerminalTokens.TokenNamesuper:
                case TerminalTokens.TokenNamenew:
                case TerminalTokens.TokenNameIdentifier:
                    if (scanner.startPosition <= selectionStart && selectionStart <= scanner.currentPosition) {
                        if (scanner.currentPosition == scanner.eofPosition) {
                            int temp = scanner.eofPosition;
                            scanner.eofPosition = scanner.source.length;
                            while (scanner.getNextCharAsJavaIdentifierPart()) {
                                /*empty*/}
                            scanner.eofPosition = temp;
                        }
                        lastIdentifierStart = scanner.startPosition;
                        lastIdentifierEnd = scanner.currentPosition - 1;
                        lastIdentifier = scanner.getCurrentTokenSource();
                        break isolateLastName;
                    }
                    break;
                case TerminalTokens.TokenNameARROW:
                case TerminalTokens.TokenNameCOLON_COLON:
                    if (scanner.startPosition <= selectionStart && selectionStart <= scanner.currentPosition) {
                        lastIdentifierStart = scanner.startPosition;
                        lastIdentifierEnd = scanner.currentPosition - 1;
                        lastIdentifier = scanner.getCurrentTokenSource();
                        break isolateLastName;
                    }
                    break;
                }
            } while (token != TerminalTokens.TokenNameEOF);
        } else {
            if (selectionStart == selectionEnd) { // Widen the selection to scan -> || :: if needed. No unicode handling for now.
                if (selectionStart > 0 && selectionEnd < source.length - 1) {
                    if ((source[selectionStart] == '>' && source[selectionStart - 1] == '-')
                            || source[selectionStart] == ':' && source[selectionStart - 1] == ':') {
                        selectionStart--;
                    } else {
                        if ((source[selectionStart] == '-' && source[selectionEnd + 1] == '>')
                                || source[selectionStart] == ':' && source[selectionEnd + 1] == ':') {
                            selectionEnd++;
                        }
                    }
                }
            } // there could be some innocuous widening, shouldn't matter.
            scanner.resetTo(selectionStart, selectionEnd, isModuleInfo);

            boolean expectingIdentifier = true;
            do {
                try {
                    token = scanner.getNextToken();
                } catch (InvalidInputException e) {
                    return false;
                }
                switch (token) {
                case TerminalTokens.TokenNamethis:
                case TerminalTokens.TokenNamesuper:
                case TerminalTokens.TokenNamenew:
                case TerminalTokens.TokenNameIdentifier:
                    if (!expectingIdentifier)
                        return false;
                    lastIdentifier = scanner.getCurrentTokenSource();
                    lastIdentifierStart = scanner.startPosition;
                    lastIdentifierEnd = scanner.currentPosition - 1;
                    if (lastIdentifierEnd > selectionEnd) {
                        lastIdentifierEnd = selectionEnd;
                        lastIdentifier = CharOperation.subarray(lastIdentifier, 0,
                                lastIdentifierEnd - lastIdentifierStart + 1);
                    }
                    expectingIdentifier = false;
                    break;
                case TerminalTokens.TokenNameCOLON_COLON:
                    if (selectionStart >= scanner.startPosition && selectionEnd < scanner.currentPosition) {
                        this.actualSelectionStart = selectionStart;
                        this.actualSelectionEnd = selectionEnd;
                        this.selectedIdentifier = CharOperation.NO_CHAR;
                        return true;
                    }
                    //$FALL-THROUGH$
                case TerminalTokens.TokenNameDOT:
                    if (expectingIdentifier)
                        return false;
                    expectingIdentifier = true;
                    break;
                case TerminalTokens.TokenNameEOF:
                    if (expectingIdentifier)
                        return false;
                    break;
                case TerminalTokens.TokenNameLESS:
                    if (!checkTypeArgument(scanner))
                        return false;
                    break;
                case TerminalTokens.TokenNameAT:
                    if (scanner.startPosition != scanner.initialPosition)
                        return false;
                    break;
                case TerminalTokens.TokenNameARROW:
                    if (selectionStart >= scanner.startPosition && selectionEnd < scanner.currentPosition) {
                        this.actualSelectionStart = selectionStart;
                        this.actualSelectionEnd = selectionEnd;
                        this.selectedIdentifier = CharOperation.NO_CHAR;
                        return true;
                    }
                    return false;
                default:
                    return false;
                }
            } while (token != TerminalTokens.TokenNameEOF);
        }
        if (lastIdentifierStart > 0) {
            this.actualSelectionStart = lastIdentifierStart;
            this.actualSelectionEnd = lastIdentifierEnd;
            this.selectedIdentifier = lastIdentifier;
            return true;
        }
        return false;
    }

    private boolean checkTypeArgument(Scanner scanner) {
        int depth = 1;
        int token;
        StringBuffer buffer = new StringBuffer();
        do {
            try {
                token = scanner.getNextToken();
            } catch (InvalidInputException e) {
                return false;
            }
            switch (token) {
            case TerminalTokens.TokenNameLESS:
                depth++;
                buffer.append(scanner.getCurrentTokenSource());
                break;
            case TerminalTokens.TokenNameGREATER:
                depth--;
                buffer.append(scanner.getCurrentTokenSource());
                break;
            case TerminalTokens.TokenNameRIGHT_SHIFT:
                depth -= 2;
                buffer.append(scanner.getCurrentTokenSource());
                break;
            case TerminalTokens.TokenNameUNSIGNED_RIGHT_SHIFT:
                depth -= 3;
                buffer.append(scanner.getCurrentTokenSource());
                break;
            case TerminalTokens.TokenNameextends:
            case TerminalTokens.TokenNamesuper:
                buffer.append(' ');
                buffer.append(scanner.getCurrentTokenSource());
                buffer.append(' ');
                break;
            case TerminalTokens.TokenNameCOMMA:
                if (depth == 1) {
                    int length = buffer.length();
                    char[] typeRef = new char[length];
                    buffer.getChars(0, length, typeRef, 0);
                    try {
                        Signature.createTypeSignature(typeRef, true);
                        buffer = new StringBuffer();
                    } catch (IllegalArgumentException e) {
                        return false;
                    }
                }
                break;
            default:
                buffer.append(scanner.getCurrentTokenSource());
                break;

            }
            if (depth < 0) {
                return false;
            }
        } while (depth != 0 && token != TerminalTokens.TokenNameEOF);

        if (depth == 0) {
            int length = buffer.length() - 1;
            char[] typeRef = new char[length];
            buffer.getChars(0, length, typeRef, 0);
            try {
                Signature.createTypeSignature(typeRef, true);
                return true;
            } catch (IllegalArgumentException e) {
                return false;
            }
        }

        return false;
    }

    /*
     * find all types outside the project scope
     */
    private void findAllTypes(char[] prefix) {
        try {
            IProgressMonitor progressMonitor = new IProgressMonitor() {
                boolean isCanceled = false;

                @Override
                public void beginTask(String name, int totalWork) {
                    // implements interface method
                }

                @Override
                public void done() {
                    // implements interface method
                }

                @Override
                public void internalWorked(double work) {
                    // implements interface method
                }

                @Override
                public boolean isCanceled() {
                    return this.isCanceled;
                }

                @Override
                public void setCanceled(boolean value) {
                    this.isCanceled = value;
                }

                @Override
                public void setTaskName(String name) {
                    // implements interface method
                }

                @Override
                public void subTask(String name) {
                    // implements interface method
                }

                @Override
                public void worked(int work) {
                    // implements interface method
                }
            };

            TypeNameMatchRequestor typeNameMatchRequestor = new TypeNameMatchRequestor() {
                @Override
                public void acceptTypeNameMatch(TypeNameMatch match) {
                    if (SelectionEngine.this.requestor instanceof SelectionRequestor) {
                        SelectionEngine.this.noProposal = false;
                        ((SelectionRequestor) SelectionEngine.this.requestor).acceptType(match.getType());
                    }
                }
            };

            IJavaSearchScope scope = BasicSearchEngine.createWorkspaceScope();

            SelectionTypeNameMatchRequestorWrapper requestorWrapper = new SelectionTypeNameMatchRequestorWrapper(
                    typeNameMatchRequestor, scope,
                    this.unitScope == null ? null : this.unitScope.referenceContext.imports);

            org.eclipse.jdt.core.ICompilationUnit[] workingCopies = this.owner == null ? null
                    : JavaModelManager.getJavaModelManager().getWorkingCopies(this.owner, true/*add primary WCs*/);

            try {
                new BasicSearchEngine(workingCopies).searchAllTypeNames(null, SearchPattern.R_EXACT_MATCH, prefix,
                        SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE, IJavaSearchConstants.TYPE,
                        scope, requestorWrapper, IJavaSearchConstants.CANCEL_IF_NOT_READY_TO_SEARCH,
                        progressMonitor);
            } catch (OperationCanceledException e) {
                // do nothing
            }
            requestorWrapper.acceptNotImported();
        } catch (JavaModelException e) {
            // do nothing
        }
    }

    @Override
    public AssistParser getParser() {
        return this.parser;
    }

    /*
     * Returns whether the given binding is a local/anonymous reference binding, or if its declaring class is
     * local.
     */
    private boolean isLocal(ReferenceBinding binding) {
        if (binding instanceof ParameterizedTypeBinding) {
            return isLocal(((ParameterizedTypeBinding) binding).genericType());
        }
        if (!(binding instanceof SourceTypeBinding))
            return false;
        if (binding instanceof LocalTypeBinding)
            return true;
        if (binding instanceof MemberTypeBinding) {
            return isLocal(((MemberTypeBinding) binding).enclosingType);
        }
        return false;
    }

    /**
     * Ask the engine to compute the selection at the specified position
     * of the given compilation unit.
        
     *  @param sourceUnit org.eclipse.jdt.internal.compiler.env.ICompilationUnit
     *      the source of the current compilation unit.
     *
     *  @param selectionSourceStart int
     *  @param selectionSourceEnd int
     *      a range in the source where the selection is.
     */
    public void select(ICompilationUnit sourceUnit, int selectionSourceStart, int selectionSourceEnd) {

        char[] source = sourceUnit.getContents();

        if (DEBUG) {
            System.out.print("SELECTION IN "); //$NON-NLS-1$
            System.out.print(sourceUnit.getFileName());
            System.out.print(" FROM "); //$NON-NLS-1$
            System.out.print(selectionSourceStart);
            System.out.print(" TO "); //$NON-NLS-1$
            System.out.println(selectionSourceEnd);
            System.out.println("SELECTION - Source :"); //$NON-NLS-1$
            System.out.println(source);
        }
        boolean isModuleInfo = CharOperation.endsWith(sourceUnit.getFileName(),
                TypeConstants.MODULE_INFO_FILE_NAME);
        if (!checkSelection(source, selectionSourceStart, selectionSourceEnd, isModuleInfo)) {
            return;
        }
        if (DEBUG) {
            System.out.print("SELECTION - Checked : \""); //$NON-NLS-1$
            System.out.print(new String(source, this.actualSelectionStart,
                    this.actualSelectionEnd - this.actualSelectionStart + 1));
            System.out.println('"');
        }
        try {
            this.acceptedAnswer = false;
            CompilationResult result = new CompilationResult(sourceUnit, 1, 1,
                    this.compilerOptions.maxProblemsPerUnit);
            CompilationUnitDeclaration parsedUnit = this.parser.dietParse(sourceUnit, result,
                    this.actualSelectionStart, this.actualSelectionEnd);

            if (parsedUnit != null) {
                if (DEBUG) {
                    System.out.println("SELECTION - Diet AST :"); //$NON-NLS-1$
                    System.out.println(parsedUnit.toString());
                }

                // scan the package & import statements first
                if (parsedUnit.currentPackage instanceof SelectionOnPackageReference) {
                    char[][] tokens = ((SelectionOnPackageReference) parsedUnit.currentPackage).tokens;
                    this.noProposal = false;
                    this.requestor.acceptPackage(CharOperation.concatWith(tokens, '.'));
                    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 SelectionOnImportReference) {
                            char[][] tokens = ((SelectionOnImportReference) importReference).tokens;
                            this.noProposal = false;
                            this.requestor.acceptPackage(CharOperation.concatWith(tokens, '.'));
                            this.nameEnvironment.findTypes(CharOperation.concatWith(tokens, '.'), false, false,
                                    IJavaSearchConstants.TYPE, this);

                            this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
                            if ((this.unitScope = parsedUnit.scope) != null) {
                                int tokenCount = tokens.length;
                                char[] lastToken = tokens[tokenCount - 1];
                                char[][] qualifierTokens = CharOperation.subarray(tokens, 0, tokenCount - 1);

                                if (qualifierTokens != null && qualifierTokens.length > 0) {
                                    Binding binding = this.unitScope.getTypeOrPackage(qualifierTokens);
                                    if (binding != null && binding instanceof ReferenceBinding) {
                                        ReferenceBinding ref = (ReferenceBinding) binding;
                                        selectMemberTypeFromImport(parsedUnit, lastToken, ref,
                                                importReference.isStatic());
                                        if (importReference.isStatic()) {
                                            selectStaticFieldFromStaticImport(parsedUnit, lastToken, ref);
                                            selectStaticMethodFromStaticImport(parsedUnit, lastToken, ref);
                                        }
                                    }
                                }
                            }

                            // accept qualified types only if no unqualified type was accepted
                            if (!this.acceptedAnswer) {
                                acceptQualifiedTypes();
                                if (!this.acceptedAnswer) {
                                    this.nameEnvironment.findTypes(this.selectedIdentifier, false, false,
                                            IJavaSearchConstants.TYPE, this);
                                    // try with simple type name
                                    if (!this.acceptedAnswer) {
                                        acceptQualifiedTypes();
                                    }
                                }
                            }
                            if (this.noProposal && this.problem != null) {
                                this.requestor.acceptError(this.problem);
                            }
                            return;
                        }
                    }
                }
                try {
                    if (parsedUnit.isModuleInfo() && parsedUnit.moduleDeclaration != null) {
                        ModuleDeclaration module = parsedUnit.moduleDeclaration;
                        this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
                        if ((this.unitScope = parsedUnit.scope) != null) {
                            this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
                        }
                        module.resolveModuleDirectives(parsedUnit.scope);
                        module.resolvePackageDirectives(parsedUnit.scope);
                        module.resolveTypeDirectives(parsedUnit.scope);
                        acceptPackageVisibilityStatements(module.exports, parsedUnit.scope);
                        acceptPackageVisibilityStatements(module.opens, parsedUnit.scope);
                    } else if (parsedUnit.types != null || parsedUnit.isPackageInfo()) {
                        if (selectDeclaration(parsedUnit))
                            return;
                        this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
                        if ((this.unitScope = parsedUnit.scope) != null) {
                            this.lookupEnvironment.completeTypeBindings(parsedUnit, true);
                            CompilationUnitDeclaration previousUnitBeingCompleted = this.lookupEnvironment.unitBeingCompleted;
                            this.lookupEnvironment.unitBeingCompleted = parsedUnit;
                            parsedUnit.scope.faultInTypes();
                            this.lookupEnvironment.unitBeingCompleted = previousUnitBeingCompleted;
                            ASTNode node = null;
                            if (parsedUnit.types != null)
                                node = parseBlockStatements(parsedUnit, selectionSourceStart);
                            if (DEBUG) {
                                System.out.println("SELECTION - AST :"); //$NON-NLS-1$
                                System.out.println(parsedUnit.toString());
                            }
                            parsedUnit.resolve();
                            if (node != null) {
                                selectLocalDeclaration(node);
                            }
                        }
                    }
                } catch (SelectionNodeFound e) {
                    if (e.binding != null) {
                        if (DEBUG) {
                            System.out.println("SELECTION - Selection binding:"); //$NON-NLS-1$
                            System.out.println(e.binding.toString());
                        }
                        // if null then we found a problem in the selection node
                        selectFrom(e.binding, parsedUnit, sourceUnit, e.isDeclaration);
                    }
                }
            }
            // only reaches here if no selection could be derived from the parsed tree
            // thus use the selected source and perform a textual type search
            if (!this.acceptedAnswer) {
                this.nameEnvironment.findTypes(this.selectedIdentifier, false, false, IJavaSearchConstants.TYPE,
                        this);

                // accept qualified types only if no unqualified type was accepted
                if (!this.acceptedAnswer) {
                    acceptQualifiedTypes();

                    // accept types from all the workspace only if no type was found in the project scope
                    if (this.noProposal) {
                        findAllTypes(this.selectedIdentifier);
                    }
                }
            }
            if (this.noProposal && this.problem != null) {
                this.requestor.acceptError(this.problem);
            }
        } catch (IndexOutOfBoundsException | AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
            if (DEBUG) {
                System.out.println("Exception caught by SelectionEngine:"); //$NON-NLS-1$
                e.printStackTrace(System.out);
            }
        } finally {
            reset(true);
        }
    }

    private void acceptPackageVisibilityStatements(PackageVisibilityStatement[] pvs, Scope scope) {
        if (pvs != null) {
            for (PackageVisibilityStatement pv : pvs) {
                if (pv.pkgRef instanceof SelectionOnPackageVisibilityReference) {
                    this.noProposal = false;
                    this.requestor.acceptPackage(CharOperation
                            .concatWith(((SelectionOnPackageVisibilityReference) pv.pkgRef).tokens, '.'));
                }
            }
        }
    }

    private void selectMemberTypeFromImport(CompilationUnitDeclaration parsedUnit, char[] lastToken,
            ReferenceBinding ref, boolean staticOnly) {
        int fieldLength = lastToken.length;
        ReferenceBinding[] memberTypes = ref.memberTypes();
        next: for (int j = 0; j < memberTypes.length; j++) {
            ReferenceBinding memberType = memberTypes[j];

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

            if (staticOnly && !memberType.isStatic())
                continue next;

            if (!CharOperation.equals(lastToken, memberType.sourceName, true))
                continue next;

            selectFrom(memberType, parsedUnit, false);
        }
    }

    private void selectStaticFieldFromStaticImport(CompilationUnitDeclaration parsedUnit, char[] lastToken,
            ReferenceBinding ref) {
        int fieldLength = lastToken.length;
        FieldBinding[] fields = ref.availableFields();
        next: for (int j = 0; j < fields.length; j++) {
            FieldBinding field = fields[j];

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

            if (field.isSynthetic())
                continue next;

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

            if (!CharOperation.equals(lastToken, field.name, true))
                continue next;

            selectFrom(field, parsedUnit, false);
        }
    }

    private void selectStaticMethodFromStaticImport(CompilationUnitDeclaration parsedUnit, char[] lastToken,
            ReferenceBinding ref) {
        int methodLength = lastToken.length;
        MethodBinding[] methods = ref.availableMethods();
        next: for (int j = 0; j < methods.length; j++) {
            MethodBinding method = methods[j];

            if (method.isSynthetic())
                continue next;

            if (method.isDefaultAbstract())
                continue next;

            if (method.isConstructor())
                continue next;

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

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

            if (!CharOperation.equals(lastToken, method.selector, true))
                continue next;

            selectFrom(method, parsedUnit, false);
        }
    }

    private void selectFrom(Binding binding, CompilationUnitDeclaration parsedUnit, boolean isDeclaration) {
        selectFrom(binding, parsedUnit, null, isDeclaration);
    }

    private void selectFrom(Binding binding, CompilationUnitDeclaration parsedUnit, ICompilationUnit unit,
            boolean isDeclaration) {
        if (binding instanceof TypeVariableBinding) {
            TypeVariableBinding typeVariableBinding = (TypeVariableBinding) binding;
            Binding enclosingElement = typeVariableBinding.declaringElement;
            this.noProposal = false;

            if (enclosingElement instanceof SourceTypeBinding) {
                SourceTypeBinding enclosingType = (SourceTypeBinding) enclosingElement;
                if (isLocal(enclosingType) && this.requestor instanceof SelectionRequestor) {
                    ((SelectionRequestor) this.requestor).acceptLocalTypeParameter(typeVariableBinding);
                } else {
                    this.requestor.acceptTypeParameter(enclosingType.qualifiedPackageName(),
                            enclosingType.qualifiedSourceName(), typeVariableBinding.sourceName(), false,
                            this.actualSelectionStart, this.actualSelectionEnd);
                }
            } else if (enclosingElement instanceof MethodBinding) {
                MethodBinding enclosingMethod = (MethodBinding) enclosingElement;
                if (isLocal(enclosingMethod.declaringClass) && this.requestor instanceof SelectionRequestor) {
                    ((SelectionRequestor) this.requestor).acceptLocalMethodTypeParameter(typeVariableBinding);
                } else {
                    this.requestor.acceptMethodTypeParameter(enclosingMethod.declaringClass.qualifiedPackageName(),
                            enclosingMethod.declaringClass.qualifiedSourceName(),
                            enclosingMethod.isConstructor() ? enclosingMethod.declaringClass.sourceName()
                                    : enclosingMethod.selector,
                            enclosingMethod.sourceStart(), enclosingMethod.sourceEnd(),
                            typeVariableBinding.sourceName(), false, this.actualSelectionStart,
                            this.actualSelectionEnd);
                }
            }
            this.acceptedAnswer = true;
        } else if (binding instanceof ReferenceBinding) {
            ReferenceBinding typeBinding = (ReferenceBinding) binding;
            if (typeBinding instanceof ProblemReferenceBinding) {
                TypeBinding closestMatch = typeBinding.closestMatch();
                if (closestMatch instanceof ReferenceBinding) {
                    typeBinding = (ReferenceBinding) closestMatch;
                } else {
                    typeBinding = null;
                }
            }
            if (typeBinding == null)
                return;
            if (isLocal(typeBinding) && this.requestor instanceof SelectionRequestor) {
                this.noProposal = false;
                ((SelectionRequestor) this.requestor).acceptLocalType(typeBinding);
            } else if (binding instanceof IntersectionTypeBinding18) {
                IntersectionTypeBinding18 intersection = (IntersectionTypeBinding18) binding;
                ReferenceBinding[] intersectingTypes = intersection.intersectingTypes;
                for (ReferenceBinding referenceBinding : intersectingTypes) {
                    selectFrom(referenceBinding, parsedUnit, isDeclaration);
                }
            } else {
                this.noProposal = false;

                this.requestor.acceptType(typeBinding.qualifiedPackageName(), typeBinding.qualifiedSourceName(),
                        typeBinding.modifiers, false, typeBinding.computeUniqueKey(), this.actualSelectionStart,
                        this.actualSelectionEnd);
            }
            this.acceptedAnswer = true;
        } else if (binding instanceof MethodBinding) {
            MethodBinding methodBinding = getCorrectMethodBinding((MethodBinding) binding);
            this.noProposal = false;

            boolean isValuesOrValueOf = false;
            if (binding instanceof SyntheticMethodBinding) {
                SyntheticMethodBinding syntheticMethodBinding = (SyntheticMethodBinding) binding;
                if (syntheticMethodBinding.purpose == SyntheticMethodBinding.EnumValues
                        || syntheticMethodBinding.purpose == SyntheticMethodBinding.EnumValueOf) {
                    isValuesOrValueOf = true;
                }
            }

            if (!isValuesOrValueOf && !methodBinding.isSynthetic()) {
                TypeBinding[] parameterTypes = methodBinding.original().parameters;
                int length = parameterTypes.length;
                char[][] parameterPackageNames = new char[length][];
                char[][] parameterTypeNames = new char[length][];
                String[] parameterSignatures = new String[length];
                for (int i = 0; i < length; i++) {
                    parameterPackageNames[i] = parameterTypes[i].qualifiedPackageName();
                    parameterTypeNames[i] = parameterTypes[i].qualifiedSourceName();
                    parameterSignatures[i] = new String(getSignature(parameterTypes[i])).replace('/', '.');
                }

                TypeVariableBinding[] typeVariables = methodBinding.original().typeVariables;
                length = typeVariables == null ? 0 : typeVariables.length;
                char[][] typeParameterNames = new char[length][];
                char[][][] typeParameterBoundNames = new char[length][][];
                for (int i = 0; i < length; i++) {
                    TypeVariableBinding typeVariable = typeVariables[i];
                    typeParameterNames[i] = typeVariable.sourceName;
                    if (typeVariable.firstBound == null) {
                        typeParameterBoundNames[i] = new char[0][];
                    } else if (TypeBinding.equalsEquals(typeVariable.firstBound, typeVariable.superclass)) {
                        int boundCount = 1
                                + (typeVariable.superInterfaces == null ? 0 : typeVariable.superInterfaces.length);
                        typeParameterBoundNames[i] = new char[boundCount][];
                        typeParameterBoundNames[i][0] = typeVariable.superclass.sourceName;
                        for (int j = 1; j < boundCount; j++) {
                            typeParameterBoundNames[i][j] = typeVariables[i].superInterfaces[j - 1].sourceName;
                        }
                    } else {
                        int boundCount = typeVariable.superInterfaces == null ? 0
                                : typeVariable.superInterfaces.length;
                        typeParameterBoundNames[i] = new char[boundCount][];
                        for (int j = 0; j < boundCount; j++) {
                            typeParameterBoundNames[i][j] = typeVariables[i].superInterfaces[j].sourceName;
                        }
                    }
                }

                ReferenceBinding declaringClass = methodBinding.declaringClass;
                if (isLocal(declaringClass) && this.requestor instanceof SelectionRequestor) {
                    ((SelectionRequestor) this.requestor).acceptLocalMethod(methodBinding);
                } else {
                    this.requestor.acceptMethod(declaringClass.qualifiedPackageName(),
                            declaringClass.qualifiedSourceName(),
                            declaringClass.enclosingType() == null ? null
                                    : new String(getSignature(declaringClass.enclosingType())),
                            methodBinding.isConstructor() ? declaringClass.sourceName() : methodBinding.selector,
                            parameterPackageNames, parameterTypeNames, parameterSignatures, typeParameterNames,
                            typeParameterBoundNames, methodBinding.isConstructor(), isDeclaration,
                            methodBinding.computeUniqueKey(), this.actualSelectionStart, this.actualSelectionEnd);
                }
            }
            this.acceptedAnswer = true;
        } else if (binding instanceof FieldBinding) {
            FieldBinding fieldBinding = (FieldBinding) binding;
            ReferenceBinding declaringClass = fieldBinding.declaringClass;
            if (declaringClass != null) { // arraylength
                this.noProposal = false;
                if (isLocal(declaringClass) && this.requestor instanceof SelectionRequestor) {
                    ((SelectionRequestor) this.requestor).acceptLocalField(fieldBinding);
                } else {
                    // if the binding is a problem field binding, we want to make sure
                    // we can retrieve the closestMatch if the problem reason is NotVisible
                    FieldBinding currentFieldBinding = fieldBinding;
                    while (currentFieldBinding instanceof ProblemFieldBinding) {
                        ProblemFieldBinding problemFieldBinding = (ProblemFieldBinding) currentFieldBinding;
                        if (problemFieldBinding.problemId() == ProblemReasons.NotVisible) {
                            currentFieldBinding = problemFieldBinding.closestMatch;
                        } else {
                            currentFieldBinding = null;
                        }
                    }
                    char[] fieldName = null;
                    char[] key = null;
                    if (currentFieldBinding != null) {
                        fieldName = currentFieldBinding.name;
                        key = currentFieldBinding.computeUniqueKey();
                    } else {
                        fieldName = fieldBinding.name;
                        key = fieldBinding.computeUniqueKey();
                    }
                    this.requestor.acceptField(declaringClass.qualifiedPackageName(),
                            declaringClass.qualifiedSourceName(), fieldName, false, key, this.actualSelectionStart,
                            this.actualSelectionEnd);
                }
                this.acceptedAnswer = true;
            }
        } else if (binding instanceof LocalVariableBinding) {
            if (this.requestor instanceof SelectionRequestor) {
                ((SelectionRequestor) this.requestor).acceptLocalVariable((LocalVariableBinding) binding, unit);
                this.acceptedAnswer = true;
            } else {
                // open on the type of the variable
                selectFrom(((LocalVariableBinding) binding).type, parsedUnit, false);
            }
        } else if (binding instanceof ArrayBinding) {
            selectFrom(((ArrayBinding) binding).leafComponentType, parsedUnit, false);
            // open on the type of the array
        } else if (binding instanceof PackageBinding) {
            PackageBinding packageBinding = (PackageBinding) binding;
            this.noProposal = false;
            this.requestor.acceptPackage(packageBinding.readableName());
            this.acceptedAnswer = true;
        } else if (binding instanceof BaseTypeBinding) {
            this.acceptedAnswer = true;
        } else if (binding instanceof ModuleBinding) {
            this.noProposal = false;
            ModuleBinding moduleBinding = (ModuleBinding) binding;
            this.requestor.acceptModule(moduleBinding.moduleName, moduleBinding.computeUniqueKey(),
                    this.actualSelectionStart, this.actualSelectionEnd);
            this.acceptedAnswer = true;
        }
    }

    /*
     * Checks if a local declaration got selected in this method/initializer/field.
     */
    private void selectLocalDeclaration(ASTNode node) {
        // the selected identifier is not identical to the parser one (equals but not identical),
        // for traversing the parse tree, the parser assist identifier is necessary for identitiy checks
        final char[] assistIdentifier = getParser().assistIdentifier();
        if (assistIdentifier == null)
            return;

        class Visitor extends ASTVisitor {
            @Override
            public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
                if (constructorDeclaration.selector == assistIdentifier) {
                    if (constructorDeclaration.binding != null) {
                        throw new SelectionNodeFound(constructorDeclaration.binding);
                    } else {
                        if (constructorDeclaration.scope != null) {
                            throw new SelectionNodeFound(new MethodBinding(constructorDeclaration.modifiers,
                                    constructorDeclaration.selector, null, null, null,
                                    constructorDeclaration.scope.referenceType().binding));
                        }
                    }
                }
                return true;
            }

            @Override
            public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
                if (localDeclaration.type instanceof SingleTypeReference
                        && ((SingleTypeReference) localDeclaration.type).token == assistIdentifier) {
                    if (localDeclaration.binding != null) {
                        throw new SelectionNodeFound(localDeclaration.binding.type);
                    } else {
                        throw new SelectionNodeFound();
                    }
                }
                return true; // do nothing by default, keep traversing
            }

            @Override
            public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
                if (fieldDeclaration.name == assistIdentifier) {
                    throw new SelectionNodeFound(fieldDeclaration.binding);
                }
                return true;
            }

            @Override
            public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
                if (localTypeDeclaration.name == assistIdentifier) {
                    throw new SelectionNodeFound(localTypeDeclaration.binding);
                }
                return true;
            }

            @Override
            public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
                if (memberTypeDeclaration.name == assistIdentifier) {
                    throw new SelectionNodeFound(memberTypeDeclaration.binding);
                }
                return true;
            }

            @Override
            public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
                if (methodDeclaration.selector == assistIdentifier) {
                    if (methodDeclaration.binding != null) {
                        throw new SelectionNodeFound(methodDeclaration.binding);
                    } else {
                        if (methodDeclaration.scope != null) {
                            throw new SelectionNodeFound(
                                    new MethodBinding(methodDeclaration.modifiers, methodDeclaration.selector, null,
                                            null, null, methodDeclaration.scope.referenceType().binding));
                        }
                    }
                }
                return true;
            }

            @Override
            public boolean visit(Argument argument, BlockScope scope) {
                if (argument.type instanceof SingleTypeReference
                        && ((SingleTypeReference) argument.type).token == assistIdentifier)
                    throw new SelectionNodeFound(argument.binding.type);
                return true; // do nothing by default, keep traversing
            }

            @Override
            public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
                if (typeDeclaration.name == assistIdentifier) {
                    throw new SelectionNodeFound(typeDeclaration.binding);
                }
                return true;
            }

            @Override
            public boolean visit(TypeParameter typeParameter, BlockScope scope) {
                if (typeParameter.name == assistIdentifier) {
                    throw new SelectionNodeFound(typeParameter.binding);
                }
                return true;
            }

            @Override
            public boolean visit(TypeParameter typeParameter, ClassScope scope) {
                if (typeParameter.name == assistIdentifier) {
                    throw new SelectionNodeFound(typeParameter.binding);
                }
                return true;
            }
        }

        if (node instanceof AbstractMethodDeclaration) {
            ((AbstractMethodDeclaration) node).traverse(new Visitor(), (ClassScope) null);
        } else {
            ((FieldDeclaration) node).traverse(new Visitor(), (MethodScope) null);
        }
    }

    /**
     * Asks the engine to compute the selection of the given type
     * from the given context
     *
     *  @param typeName char[]
     *      a type name which is to be resolved in the context of a compilation unit.
     *      NOTE: the type name is supposed to be correctly reduced (no whitespaces, no unicodes left)
     *
     *  @param context org.eclipse.jdt.core.IType
     *      the context in which code assist is invoked.
     */
    public void selectType(char[] typeName, IType context) throws JavaModelException {
        try {
            this.acceptedAnswer = false;

            // only the type erasure are returned by IType.resolvedType(...)
            if (CharOperation.indexOf('<', typeName) != -1) {
                char[] typeSig = Signature.createCharArrayTypeSignature(typeName, false/*not resolved*/);
                typeSig = Signature.getTypeErasure(typeSig);
                typeName = Signature.toCharArray(typeSig);
            }

            CompilationUnitDeclaration parsedUnit = null;
            TypeDeclaration typeDeclaration = null;
            org.eclipse.jdt.core.ICompilationUnit cu = context.getCompilationUnit();
            if (cu != null) {
                IType[] topLevelTypes = cu.getTypes();
                int length = topLevelTypes.length;
                SourceTypeElementInfo[] topLevelInfos = new SourceTypeElementInfo[length];
                for (int i = 0; i < length; i++) {
                    topLevelInfos[i] = (SourceTypeElementInfo) ((SourceType) topLevelTypes[i]).getElementInfo();
                }
                CompilationResult result = new CompilationResult(
                        (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) cu, 1, 1,
                        this.compilerOptions.maxProblemsPerUnit);
                int flags = SourceTypeConverter.FIELD_AND_METHOD | SourceTypeConverter.MEMBER_TYPE;
                if (context.isAnonymous() || context.isLocal())
                    flags |= SourceTypeConverter.LOCAL_TYPE;
                parsedUnit = SourceTypeConverter.buildCompilationUnit(topLevelInfos, flags,
                        this.parser.problemReporter(), result);
                if (parsedUnit != null && parsedUnit.types != null) {
                    if (DEBUG) {
                        System.out.println("SELECTION - Diet AST :"); //$NON-NLS-1$
                        System.out.println(parsedUnit.toString());
                    }
                    // find the type declaration that corresponds to the original source type
                    while (context.isLambda() && context.getParent() != null) {
                        // It is easier to find the first enclosing proper type than the corresponding 
                        // lambda expression ast to add the selection node to.
                        context = (IType) context.getParent().getAncestor(IJavaElement.TYPE);
                    }
                    typeDeclaration = new ASTNodeFinder(parsedUnit).findType(context);
                }
            } else { // binary type
                IOrdinaryClassFile iClassFile = context.getClassFile();
                if (iClassFile instanceof ClassFile) {
                    ClassFile classFile = (ClassFile) iClassFile;
                    ClassFileReader reader = null;
                    if (classFile.getPackageFragmentRoot() instanceof JrtPackageFragmentRoot) {
                        IBinaryType binaryTypeInfo = classFile.getBinaryTypeInfo();
                        if (binaryTypeInfo instanceof ClassFileReader) {
                            reader = (ClassFileReader) binaryTypeInfo;
                        }
                    } else {
                        BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(classFile);
                        try {
                            reader = BinaryTypeFactory.rawReadType(descriptor,
                                    false/*don't fully initialize so as to keep constant pool (used below)*/);
                        } catch (ClassFormatException e) {
                            if (JavaCore.getPlugin().isDebugging()) {
                                e.printStackTrace(System.err);
                            }
                        }
                    }
                    if (reader == null) {
                        throw classFile.newNotPresentException();
                    }
                    CompilationResult result = new CompilationResult(reader.getFileName(), 1, 1,
                            this.compilerOptions.maxProblemsPerUnit);
                    parsedUnit = new CompilationUnitDeclaration(this.parser.problemReporter(), result, 0);
                    HashSetOfCharArrayArray typeNames = new HashSetOfCharArrayArray();

                    BinaryTypeConverter converter = new BinaryTypeConverter(this.parser.problemReporter(), result,
                            typeNames);
                    typeDeclaration = converter.buildTypeDeclaration(context, parsedUnit);
                    parsedUnit.imports = converter.buildImports(reader);
                }
            }

            if (typeDeclaration != null) {

                // add fake field with the type we're looking for
                // note: since we didn't ask for fields above, there is no field defined yet
                FieldDeclaration field = new FieldDeclaration();
                int dot;
                if ((dot = CharOperation.lastIndexOf('.', typeName)) == -1) {
                    this.selectedIdentifier = typeName;
                    field.type = new SelectionOnSingleTypeReference(typeName, -1);
                    // position not used
                } else {
                    char[][] previousIdentifiers = CharOperation.splitOn('.', typeName, 0, dot);
                    char[] selectionIdentifier = CharOperation.subarray(typeName, dot + 1, typeName.length);
                    this.selectedIdentifier = selectionIdentifier;
                    field.type = new SelectionOnQualifiedTypeReference(previousIdentifiers, selectionIdentifier,
                            new long[previousIdentifiers.length + 1]);
                }
                field.name = "<fakeField>".toCharArray(); //$NON-NLS-1$
                typeDeclaration.fields = new FieldDeclaration[] { field };

                // build bindings
                this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/);
                if ((this.unitScope = parsedUnit.scope) != null) {
                    try {
                        // build fields
                        // note: this builds fields only in the parsed unit (the buildFieldsAndMethods flag is not passed along)
                        this.lookupEnvironment.completeTypeBindings(parsedUnit, true);

                        // resolve
                        parsedUnit.scope.faultInTypes();
                        parsedUnit.resolve();
                    } catch (SelectionNodeFound e) {
                        if (e.binding != null) {
                            if (DEBUG) {
                                System.out.println("SELECTION - Selection binding :"); //$NON-NLS-1$
                                System.out.println(e.binding.toString());
                            }
                            // if null then we found a problem in the selection node
                            selectFrom(e.binding, parsedUnit, e.isDeclaration);
                        }
                    }
                }
            }
            if (this.noProposal && this.problem != null) {
                this.requestor.acceptError(this.problem);
            }
        } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object
        } finally {
            reset(true);
        }
    }

    // Check if a declaration got selected in this unit
    private boolean selectDeclaration(CompilationUnitDeclaration compilationUnit) {

        // the selected identifier is not identical to the parser one (equals but not identical),
        // for traversing the parse tree, the parser assist identifier is necessary for identitiy checks
        char[] assistIdentifier = getParser().assistIdentifier();
        if (assistIdentifier == null)
            return false;

        ImportReference currentPackage = compilationUnit.currentPackage;
        char[] packageName = currentPackage == null ? CharOperation.NO_CHAR
                : CharOperation.concatWith(currentPackage.tokens, '.');
        // iterate over the types
        TypeDeclaration[] types = compilationUnit.types;
        for (int i = 0, length = types == null ? 0 : types.length; i < length; i++) {
            if (selectDeclaration(types[i], assistIdentifier, packageName))
                return true;
        }
        return false;
    }

    // Check if a declaration got selected in this type
    private boolean selectDeclaration(TypeDeclaration typeDeclaration, char[] assistIdentifier,
            char[] packageName) {

        if (typeDeclaration.name == assistIdentifier) {
            char[] qualifiedSourceName = null;

            TypeDeclaration enclosingType = typeDeclaration;
            while (enclosingType != null) {
                qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
                enclosingType = enclosingType.enclosingType;
            }
            char[] uniqueKey = typeDeclaration.binding != null ? typeDeclaration.binding.computeUniqueKey() : null;

            this.requestor.acceptType(packageName, qualifiedSourceName, typeDeclaration.modifiers, true, uniqueKey,
                    this.actualSelectionStart, this.actualSelectionEnd);

            this.noProposal = false;
            return true;
        }
        TypeDeclaration[] memberTypes = typeDeclaration.memberTypes;
        for (int i = 0, length = memberTypes == null ? 0 : memberTypes.length; i < length; i++) {
            if (selectDeclaration(memberTypes[i], assistIdentifier, packageName))
                return true;
        }
        FieldDeclaration[] fields = typeDeclaration.fields;
        for (int i = 0, length = fields == null ? 0 : fields.length; i < length; i++) {
            if (fields[i].name == assistIdentifier) {
                char[] qualifiedSourceName = null;

                TypeDeclaration enclosingType = typeDeclaration;
                while (enclosingType != null) {
                    qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
                    enclosingType = enclosingType.enclosingType;
                }
                FieldDeclaration field = fields[i];
                this.requestor.acceptField(packageName, qualifiedSourceName, field.name, true,
                        field.binding != null ? field.binding.computeUniqueKey() : null, this.actualSelectionStart,
                        this.actualSelectionEnd);

                this.noProposal = false;
                return true;
            }
        }
        AbstractMethodDeclaration[] methods = typeDeclaration.methods;
        for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++) {
            AbstractMethodDeclaration method = methods[i];

            if (method.selector == assistIdentifier) {
                char[] qualifiedSourceName = null;

                TypeDeclaration enclosingType = typeDeclaration;
                while (enclosingType != null) {
                    qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
                    enclosingType = enclosingType.enclosingType;
                }

                this.requestor.acceptMethod(packageName, qualifiedSourceName, null, // SelectionRequestor does not need of declaring type signature for method declaration
                        method.selector, null, // SelectionRequestor does not need of parameters type for method declaration
                        null, // SelectionRequestor does not need of parameters type for method declaration
                        null, // SelectionRequestor does not need of parameters type for method declaration
                        null, // SelectionRequestor does not need of type parameters name for method declaration
                        null, // SelectionRequestor does not need of type parameters bounds for method declaration
                        method.isConstructor(), true,
                        method.binding != null ? method.binding.computeUniqueKey() : null,
                        this.actualSelectionStart, this.actualSelectionEnd);

                this.noProposal = false;
                return true;
            }

            TypeParameter[] methodTypeParameters = method.typeParameters();
            for (int j = 0, length2 = methodTypeParameters == null ? 0
                    : methodTypeParameters.length; j < length2; j++) {
                TypeParameter methodTypeParameter = methodTypeParameters[j];

                if (methodTypeParameter.name == assistIdentifier) {
                    char[] qualifiedSourceName = null;

                    TypeDeclaration enclosingType = typeDeclaration;
                    while (enclosingType != null) {
                        qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
                        enclosingType = enclosingType.enclosingType;
                    }

                    this.requestor.acceptMethodTypeParameter(packageName, qualifiedSourceName, method.selector,
                            method.sourceStart, method.sourceEnd, methodTypeParameter.name, true,
                            this.actualSelectionStart, this.actualSelectionEnd);

                    this.noProposal = false;
                    return true;
                }
            }
        }

        TypeParameter[] typeParameters = typeDeclaration.typeParameters;
        for (int i = 0, length = typeParameters == null ? 0 : typeParameters.length; i < length; i++) {
            TypeParameter typeParameter = typeParameters[i];
            if (typeParameter.name == assistIdentifier) {
                char[] qualifiedSourceName = null;

                TypeDeclaration enclosingType = typeDeclaration;
                while (enclosingType != null) {
                    qualifiedSourceName = CharOperation.concat(enclosingType.name, qualifiedSourceName, '.');
                    enclosingType = enclosingType.enclosingType;
                }

                this.requestor.acceptTypeParameter(packageName, qualifiedSourceName, typeParameter.name, true,
                        this.actualSelectionStart, this.actualSelectionEnd);

                this.noProposal = false;
                return true;
            }
        }

        return false;
    }

    /*
     * Returns the correct method binding according to whether the selection is on the method declaration
     * or on the inheritDoc tag in its javadoc.
     */
    private MethodBinding getCorrectMethodBinding(MethodBinding binding) {
        if (this.parser.javadocParser instanceof SelectionJavadocParser) {
            if (((SelectionJavadocParser) this.parser.javadocParser).inheritDocTagSelected) {
                try {
                    Object res = findMethodWithAttachedDocInHierarchy(binding);
                    if (res instanceof MethodBinding) {
                        return (MethodBinding) res;
                    }
                } catch (JavaModelException e) {
                    return null;
                }
            }
        }
        return binding;
    }

    protected MethodBinding findOverriddenMethodInType(ReferenceBinding overriddenType, MethodBinding overriding)
            throws JavaModelException {
        if (overriddenType == null)
            return null;
        MethodBinding[] overriddenMethods = overriddenType.availableMethods();
        LookupEnvironment lookupEnv = this.lookupEnvironment;
        if (lookupEnv != null && overriddenMethods != null) {
            for (int i = 0; i < overriddenMethods.length; i++) {
                if (lookupEnv.methodVerifier().isMethodSubsignature(overriding, overriddenMethods[i])) {
                    return overriddenMethods[i];
                }
            }
        }
        return null;
    }

    private Object findMethodWithAttachedDocInHierarchy(final MethodBinding method) throws JavaModelException {
        ReferenceBinding type = method.declaringClass;
        final SelectionRequestor requestor1 = (SelectionRequestor) this.requestor;
        return new InheritDocVisitor() {
            @Override
            public Object visit(ReferenceBinding currType) throws JavaModelException {
                MethodBinding overridden = findOverriddenMethodInType(currType, method);
                if (overridden == null)
                    return InheritDocVisitor.CONTINUE;
                TypeBinding args[] = overridden.parameters;
                String names[] = new String[args.length];
                for (int i = 0; i < args.length; i++) {
                    names[i] = Signature.createTypeSignature(args[i].sourceName(), false);
                }
                IMember member = (IMember) requestor1.findMethodFromBinding(overridden, names,
                        overridden.declaringClass);
                if (member == null)
                    return InheritDocVisitor.CONTINUE;
                if (member.getAttachedJavadoc(null) != null) {
                    // for binary methods with attached javadoc and no source attached
                    return overridden;
                }
                IOpenable openable = member.getOpenable();
                if (openable == null)
                    return InheritDocVisitor.CONTINUE;
                IBuffer buf = openable.getBuffer();
                if (buf == null) {
                    // no source attachment found. This method maybe the one. Stop.
                    return InheritDocVisitor.STOP_BRANCH;
                }

                ISourceRange javadocRange = member.getJavadocRange();
                if (javadocRange == null)
                    return InheritDocVisitor.CONTINUE; // this method doesn't have javadoc, continue to look.
                String rawJavadoc = buf.getText(javadocRange.getOffset(), javadocRange.getLength());
                if (rawJavadoc != null) {
                    return overridden;
                }
                return InheritDocVisitor.CONTINUE;
            }
        }.visitInheritDoc(type);
    }

    /**
     * Implements the "Algorithm for Inheriting Method Comments" as specified for
     * <a href="http://download.oracle.com/javase/6/docs/technotes/tools/windows/javadoc.html#inheritingcomments">1.6</a>.
     *
     * <p>
     * Unfortunately, the implementation is broken in Javadoc implementations since 1.5, see
     * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6376959">Sun's bug</a>.
     * </p>
     *
     * <p>
     * We adhere to the spec.
     * </p>
     */
    static abstract class InheritDocVisitor {
        public static final Object STOP_BRANCH = new Object() {
            @Override
            public String toString() {
                return "STOP_BRANCH"; //$NON-NLS-1$
            }
        };
        public static final Object CONTINUE = new Object() {
            @Override
            public String toString() {
                return "CONTINUE"; //$NON-NLS-1$
            }
        };

        /**
         * Visits a type and decides how the visitor should proceed.
         *
         * @param currType the current type
         * @return <ul>
         *         <li>{@link #STOP_BRANCH} to indicate that no Javadoc has been found and visiting
         *         super types should stop here</li>
         *         <li>{@link #CONTINUE} to indicate that no Javadoc has been found and visiting
         *         super types should continue</li>
         *         <li>an {@link Object} or <code>null</code>, to indicate that visiting should be
         *         cancelled immediately. The returned value is the result of
         *         {@link #visitInheritDoc(ReferenceBinding)}</li>
         *         </ul>
         * @throws JavaModelException unexpected problem
         * @see #visitInheritDoc(ReferenceBinding)
         */
        public abstract Object visit(ReferenceBinding currType) throws JavaModelException;

        /**
         * Visits the super types of the given <code>currentType</code>.
         *
         * @param currentType the starting type
         * @return the result from a call to {@link #visit(ReferenceBinding)}, or <code>null</code> if none of
         *         the calls returned a result
         * @throws JavaModelException unexpected problem
         */
        public Object visitInheritDoc(ReferenceBinding currentType) throws JavaModelException {
            ArrayList visited = new ArrayList();
            visited.add(currentType);
            Object result = visitInheritDocInterfaces(visited, currentType);
            if (result != InheritDocVisitor.CONTINUE)
                return result;

            ReferenceBinding superClass = currentType.superclass();

            while (superClass != null && !visited.contains(superClass)) {
                result = visit(superClass);
                if (result == InheritDocVisitor.STOP_BRANCH) {
                    return null;
                } else if (result == InheritDocVisitor.CONTINUE) {
                    visited.add(superClass);
                    result = visitInheritDocInterfaces(visited, superClass);
                    if (result != InheritDocVisitor.CONTINUE)
                        return result;
                    else
                        superClass = superClass.superclass();
                } else {
                    return result;
                }
            }

            return null;
        }

        /**
         * Visits the super interfaces of the given type in the given hierarchy, thereby skipping already visited types.
         * 
         * @param visited set of visited types
         * @param currentType type whose super interfaces should be visited
         * @return the result, or {@link #CONTINUE} if no result has been found
         * @throws JavaModelException unexpected problem
         */
        private Object visitInheritDocInterfaces(ArrayList visited, ReferenceBinding currentType)
                throws JavaModelException {
            ArrayList toVisitChildren = new ArrayList();
            ReferenceBinding[] superInterfaces = currentType.superInterfaces();
            for (int i = 0; i < superInterfaces.length; i++) {
                ReferenceBinding superInterface = superInterfaces[i];
                if (visited.contains(superInterface))
                    continue;
                visited.add(superInterface);
                Object result = visit(superInterface);
                if (result == InheritDocVisitor.STOP_BRANCH) {
                    //skip
                } else if (result == InheritDocVisitor.CONTINUE) {
                    toVisitChildren.add(superInterface);
                } else {
                    return result;
                }
            }
            for (Iterator iter = toVisitChildren.iterator(); iter.hasNext();) {
                ReferenceBinding child = (ReferenceBinding) iter.next();
                Object result = visitInheritDocInterfaces(visited, child);
                if (result != InheritDocVisitor.CONTINUE)
                    return result;
            }
            return InheritDocVisitor.CONTINUE;
        }
    }

    @Override
    public void acceptModule(char[] moduleName) {
        // TODO Auto-generated method stub

    }
}