/******************************************************************************* * Copyright © 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * *******************************************************************************/ package org.eclipse.edt.ide.core.internal.model.codeassist; /** * 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. */ public final class SelectionEngine /* extends Engine implements ISearchRequestor */{ public static boolean DEBUG = false; // SelectionParser parser; ISelectionRequestor requestor; // boolean acceptedAnswer; // // private int actualSelectionStart; // private int actualSelectionEnd; // private char[] qualifiedSelection; // private char[] selectedIdentifier; // // private char[][][] acceptedParts; // int acceptedClassesCount; // int acceptedInterfacesCount; // // boolean noProposal = true; // IProblem 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.codeassist.ISearchableNameEnvironment // * 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( // ISearchableNameEnvironment nameEnvironment, // ISelectionRequestor requestor, // Map settings) { // // super(settings); // // this.requestor = requestor; // this.nameEnvironment = nameEnvironment; // // ProblemReporter problemReporter = // new ProblemReporter( // DefaultErrorHandlingPolicies.proceedWithAllProblems(), // this.compilerOptions, // new DefaultProblemFactory(Locale.getDefault())) { // // public IProblem createProblem( // char[] fileName, // int problemId, // String[] problemArguments, // String[] messageArguments, // int severity, // int problemStartPosition, // int problemEndPosition, // int lineNumber, // ReferenceContext referenceContext, // CompilationResult unitResult) { // IProblem problem = super.createProblem( // fileName, // problemId, // problemArguments, // messageArguments, // severity, // problemStartPosition, // problemEndPosition, // lineNumber, // referenceContext, // unitResult); // if(SelectionEngine.this.problem == null && problem.isError() && (problem.getID() & IProblem.Syntax) == 0) { // SelectionEngine.this.problem = problem; // } // // return problem; // } // }; // this.parser = new SelectionParser(problemReporter, this.compilerOptions.sourceLevel >= CompilerOptions.JDK1_4); // this.lookupEnvironment = // new LookupEnvironment(this, this.compilerOptions, problemReporter, nameEnvironment); // } // // /** // * One result of the search consists of a new class. // * @param packageName char[] // * @param className char[] // * @param modifiers int // * // * NOTE - All package and type names are presented in their readable form: // * Package names are in the form "a.b.c". // * Nested type names are in the qualified form "A.M". // * The default package is represented by an empty array. // */ // public void acceptClass(char[] packageName, char[] className, int modifiers) { // if (CharOperation.equals(className, selectedIdentifier)) { // if (qualifiedSelection != null // && !CharOperation.equals( // qualifiedSelection, // CharOperation.concat(packageName, className, '.'))) { // return; // } // // if(mustQualifyType(packageName, className)) { // char[][] acceptedClass = new char[2][]; // acceptedClass[0] = packageName; // acceptedClass[1] = className; // // if(acceptedClasses == null) { // acceptedClasses = new char[10][][]; // acceptedClassesCount = 0; // } // int length = acceptedClasses.length; // if(length == acceptedClassesCount) { // System.arraycopy(acceptedClasses, 0, acceptedClasses = new char[(length + 1)* 2][][], 0, length); // } // acceptedClasses[acceptedClassesCount++] = acceptedClass; // // } else { // noProposal = false; // requestor.acceptClass( // packageName, // className, // false); // acceptedAnswer = true; // } // } // } // // /** // * One result of the search consists of a new interface. // * // * NOTE - All package and type names are presented in their readable form: // * Package names are in the form "a.b.c". // * Nested type names are in the qualified form "A.I". // * The default package is represented by an empty array. // */ // public void acceptInterface( // char[] packageName, // char[] interfaceName, // int modifiers) { // // if (CharOperation.equals(interfaceName, selectedIdentifier)) { // if (qualifiedSelection != null // && !CharOperation.equals( // qualifiedSelection, // CharOperation.concat(packageName, interfaceName, '.'))) { // return; // } // // if(mustQualifyType(packageName, interfaceName)) { // char[][] acceptedInterface= new char[2][]; // acceptedInterface[0] = packageName; // acceptedInterface[1] = interfaceName; // // if(acceptedInterfaces == null) { // acceptedInterfaces = new char[10][][]; // acceptedInterfacesCount = 0; // } // int length = acceptedInterfaces.length; // if(length == acceptedInterfacesCount) { // System.arraycopy(acceptedInterfaces, 0, acceptedInterfaces = new char[(length + 1) * 2][][], 0, length); // } // acceptedInterfaces[acceptedInterfacesCount++] = acceptedInterface; // // } else { // noProposal = false; // requestor.acceptInterface( // packageName, // interfaceName, // false); // acceptedAnswer = true; // } // } // } // // /** // * One result of the search consists of a new package. // * @param packageName char[] // * // * NOTE - All package names are presented in their readable form: // * Package names are in the form "a.b.c". // * The default package is represented by an empty array. // */ // public void acceptPackage(char[] packageName) { // } // // private void acceptQualifiedTypes() { // if(acceptedClasses != null){ // acceptedAnswer = true; // for (int i = 0; i < acceptedClassesCount; i++) { // noProposal = false; // requestor.acceptClass( // acceptedClasses[i][0], // acceptedClasses[i][1], // true); // } // acceptedClasses = null; // acceptedClassesCount = 0; // } // if(acceptedInterfaces != null){ // acceptedAnswer = true; // for (int i = 0; i < acceptedInterfacesCount; i++) { // noProposal = false; // requestor.acceptInterface( // acceptedInterfaces[i][0], // acceptedInterfaces[i][1], // true); // } // acceptedInterfaces = null; // acceptedInterfacesCount = 0; // } // } // // /** // * One result of the search consists of a new type. // * @param packageName char[] // * @param typeName char[] // * // * NOTE - All package and type names are presented in their readable form: // * Package names are in the form "a.b.c". // * Nested type names are in the qualified form "A.M". // * The default package is represented by an empty array. // */ // public void acceptType(char[] packageName, char[] typeName) { // acceptClass(packageName, typeName, 0); // } // // private boolean checkSelection( // char[] source, // int selectionStart, // int selectionEnd) { // // Scanner scanner = new Scanner(); // scanner.setSource(source); // // int lastIdentifierStart = -1; // int lastIdentifierEnd = -1; // char[] lastIdentifier = null; // int token, identCount = 0; // StringBuffer entireSelection = new StringBuffer(selectionEnd - selectionStart + 1); // // if(selectionStart > selectionEnd){ // // // compute start position of current line // int currentPosition = selectionStart - 1; // int nextCharacterPosition = selectionStart; // char currentCharacter = ' '; // try { // 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++; // } // if ((c1 = Character.getNumericValue(source[pos++])) > 15 // || c1 < 0 // || (c2 = Character.getNumericValue(source[pos++])) > 15 // || c2 < 0 // || (c3 = Character.getNumericValue(source[pos++])) > 15 // || c3 < 0 // || (c4 = Character.getNumericValue(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; // } // // if(currentCharacter == '\r' || currentCharacter == '\n') { // break; // } // currentPosition--; // } // } catch (ArrayIndexOutOfBoundsException e) { // return false; // } // // // compute start and end of the last token // scanner.resetTo(nextCharacterPosition, selectionEnd + 1); // do { // try { // token = scanner.getNextToken(); // } catch (InvalidInputException e) { // return false; // } // if((token == TerminalTokens.TokenNamethis || // token == TerminalTokens.TokenNamesuper || // token == TerminalTokens.TokenNameIdentifier) && // scanner.startPosition <= selectionStart && // selectionStart <= scanner.currentPosition) { // lastIdentifierStart = scanner.startPosition; // lastIdentifierEnd = scanner.currentPosition - 1; // lastIdentifier = scanner.getCurrentTokenSource(); // } // } while (token != TerminalTokens.TokenNameEOF); // } else { // scanner.resetTo(selectionStart, selectionEnd); // // boolean expectingIdentifier = true; // // do { // try { // token = scanner.getNextToken(); // } catch (InvalidInputException e) { // return false; // } // switch (token) { // case TerminalTokens.TokenNamethis : // case TerminalTokens.TokenNamesuper : // 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); // } // entireSelection.append(lastIdentifier); // // identCount++; // expectingIdentifier = false; // break; // case TerminalTokens.TokenNameDOT : // if (expectingIdentifier) // return false; // entireSelection.append('.'); // expectingIdentifier = true; // break; // case TerminalTokens.TokenNameEOF : // if (expectingIdentifier) // return false; // break; // default : // return false; // } // } while (token != TerminalTokens.TokenNameEOF); // } // if (lastIdentifierStart > 0) { // actualSelectionStart = lastIdentifierStart; // actualSelectionEnd = lastIdentifierEnd; // selectedIdentifier = lastIdentifier; // if (identCount > 1) // qualifiedSelection = entireSelection.toString().toCharArray(); // return true; // } // return false; // } // // public AssistParser getParser() { // return parser; // } // // /** // * 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); // } // if (!checkSelection(source, selectionSourceStart, selectionSourceEnd)) // return; // try { // acceptedAnswer = false; // CompilationResult result = new CompilationResult(sourceUnit, 1, 1, this.compilerOptions.maxProblemsPerUnit); // CompilationUnitDeclaration parsedUnit = // parser.dietParse(sourceUnit, result, actualSelectionStart, 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; // noProposal = false; // 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; // noProposal = false; // requestor.acceptPackage(CharOperation.concatWith(tokens, '.')); // nameEnvironment.findTypes(CharOperation.concatWith(tokens, '.'), this); // // accept qualified types only if no unqualified type was accepted // if(!acceptedAnswer) { // acceptQualifiedTypes(); // if (!acceptedAnswer) { // nameEnvironment.findTypes(selectedIdentifier, this); // // try with simple type name // if(!acceptedAnswer) { // acceptQualifiedTypes(); // } // } // } // if(noProposal && problem != null) { // requestor.acceptError(problem); // } // return; // } // } // } // if (parsedUnit.types != null) { // lookupEnvironment.buildTypeBindings(parsedUnit); // if ((this.unitScope = parsedUnit.scope) != null) { // try { // lookupEnvironment.completeTypeBindings(parsedUnit, true); // parsedUnit.scope.faultInTypes(); // selectDeclaration(parsedUnit); // parseMethod(parsedUnit, selectionSourceStart); // if(DEBUG) { // System.out.println("SELECTION - AST :"); //$NON-NLS-1$ // System.out.println(parsedUnit.toString()); // } // 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); // } // } // } // } // } // // 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 (!acceptedAnswer) { // nameEnvironment.findTypes(selectedIdentifier, this); // // // accept qualified types only if no unqualified type was accepted // if(!acceptedAnswer) { // acceptQualifiedTypes(); // } // } // if(noProposal && problem != null) { // requestor.acceptError(problem); // } // } catch (IndexOutOfBoundsException e) { // work-around internal failure - 1GEMF6D // } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object // } finally { // reset(); // } // } // // private void selectFrom(Binding binding) { // if (binding instanceof ReferenceBinding) { // ReferenceBinding typeBinding = (ReferenceBinding) binding; // if (qualifiedSelection != null // && !CharOperation.equals(qualifiedSelection, typeBinding.readableName())) { // return; // } // if (typeBinding.isInterface()) { // noProposal = false; // requestor.acceptInterface( // typeBinding.qualifiedPackageName(), // typeBinding.qualifiedSourceName(), // false); // } else if(typeBinding instanceof ProblemReferenceBinding){ // ProblemReferenceBinding problemBinding = (ProblemReferenceBinding)typeBinding; // if(problemBinding.original == null // || !(problemBinding.original instanceof ReferenceBinding)) { // return; // } // ReferenceBinding original = (ReferenceBinding) problemBinding.original; // noProposal = false; // requestor.acceptClass( // original.qualifiedPackageName(), // original.qualifiedSourceName(), // false); // } else { // noProposal = false; // requestor.acceptClass( // typeBinding.qualifiedPackageName(), // typeBinding.qualifiedSourceName(), // false); // } // acceptedAnswer = true; // } else // if (binding instanceof MethodBinding) { // MethodBinding methodBinding = (MethodBinding) binding; // TypeBinding[] parameterTypes = methodBinding.parameters; // int length = parameterTypes.length; // char[][] parameterPackageNames = new char[length][]; // char[][] parameterTypeNames = new char[length][]; // for (int i = 0; i < length; i++) { // parameterPackageNames[i] = parameterTypes[i].qualifiedPackageName(); // parameterTypeNames[i] = parameterTypes[i].qualifiedSourceName(); // } // noProposal = false; // requestor.acceptMethod( // methodBinding.declaringClass.qualifiedPackageName(), // methodBinding.declaringClass.qualifiedSourceName(), // methodBinding.isConstructor() // ? methodBinding.declaringClass.sourceName() // : methodBinding.selector, // parameterPackageNames, // parameterTypeNames, // methodBinding.isConstructor()); // acceptedAnswer = true; // } else // if (binding instanceof FieldBinding) { // FieldBinding fieldBinding = (FieldBinding) binding; // if (fieldBinding.declaringClass != null) { // arraylength // noProposal = false; // requestor.acceptField( // fieldBinding.declaringClass.qualifiedPackageName(), // fieldBinding.declaringClass.qualifiedSourceName(), // fieldBinding.name); // acceptedAnswer = true; // } // } else // if (binding instanceof LocalVariableBinding) { // selectFrom(((LocalVariableBinding) binding).type); // // open on the type of the variable // } else // if (binding instanceof ArrayBinding) { // selectFrom(((ArrayBinding) binding).leafComponentType); // // open on the type of the array // } else // if (binding instanceof PackageBinding) { // PackageBinding packageBinding = (PackageBinding) binding; // noProposal = false; // requestor.acceptPackage(packageBinding.readableName()); // acceptedAnswer = true; // } else // if(binding instanceof BaseTypeBinding) { // acceptedAnswer = true; // } // } // // /** // * Asks the engine to compute the selection of the given type // * from the source type. // * // * @param sourceType org.eclipse.jdt.internal.compiler.env.ISourceType // * a source form of the current type in which code assist is invoked. // * // * @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 topLevelTypes org.eclipse.jdt.internal.compiler.env.ISourceType[] // * a source form of the top level types of the compilation unit in which code assist is invoked. // // * @param searchInEnvironment // * if <code>true</code> and no selection could be found in context then search type in environment. // */ // public void selectType(ISourceType sourceType, char[] typeName, ISourceType[] topLevelTypes, boolean searchInEnvironment) { // try { // acceptedAnswer = false; // // // find the outer most type // ISourceType outerType = sourceType; // ISourceType parent = sourceType.getEnclosingType(); // while (parent != null) { // outerType = parent; // parent = parent.getEnclosingType(); // } // // compute parse tree for this most outer type // CompilationResult result = new CompilationResult(outerType.getFileName(), 1, 1, this.compilerOptions.maxProblemsPerUnit); // CompilationUnitDeclaration parsedUnit = // SourceTypeConverter.buildCompilationUnit( // topLevelTypes, // false, // no need for field and methods // true, // need member types // false, // no need for field initialization // 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 // char[] packageName = sourceType.getPackageName(); // char[] sourceTypeName = sourceType.getQualifiedName(); // // the fully qualified name without the package name // if (packageName != null) { // // remove the package name if necessary // sourceTypeName = // CharOperation.subarray( // sourceType.getQualifiedName(), // packageName.length + 1, // sourceTypeName.length); // }; // TypeDeclaration typeDecl = // parsedUnit.declarationOfType(CharOperation.splitOn('.', sourceTypeName)); // if (typeDecl != 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$ // typeDecl.fields = new FieldDeclaration[] { field }; // // // build bindings // lookupEnvironment.buildTypeBindings(parsedUnit); // 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); // } // } // } // } // } // // 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 (!acceptedAnswer && searchInEnvironment) { // if (this.selectedIdentifier != null) { // nameEnvironment.findTypes(typeName, this); // // // accept qualified types only if no unqualified type was accepted // if(!acceptedAnswer) { // acceptQualifiedTypes(); // } // } // } // if(noProposal && problem != null) { // requestor.acceptError(problem); // } // } catch (AbortCompilation e) { // ignore this exception for now since it typically means we cannot find java.lang.Object // } finally { // reset(); // } // } // // // Check if a declaration got selected in this unit // private void 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 = this.getParser().assistIdentifier(); // if (assistIdentifier == null) return; // // // iterate over the types // TypeDeclaration[] types = compilationUnit.types; // for (int i = 0, length = types == null ? 0 : types.length; i < length; i++){ // selectDeclaration(types[i], assistIdentifier); // } // } // // // Check if a declaration got selected in this type // private void selectDeclaration(TypeDeclaration typeDeclaration, char[] assistIdentifier){ // // if (typeDeclaration.name == assistIdentifier){ // throw new SelectionNodeFound(typeDeclaration.binding); // } // TypeDeclaration[] memberTypes = typeDeclaration.memberTypes; // for (int i = 0, length = memberTypes == null ? 0 : memberTypes.length; i < length; i++){ // selectDeclaration(memberTypes[i], assistIdentifier); // } // FieldDeclaration[] fields = typeDeclaration.fields; // for (int i = 0, length = fields == null ? 0 : fields.length; i < length; i++){ // if (fields[i].name == assistIdentifier){ // throw new SelectionNodeFound(fields[i].binding); // } // } // 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){ // if(method.binding != null) { // throw new SelectionNodeFound(method.binding); // } else { // if(method.scope != null) { // throw new SelectionNodeFound(new MethodBinding(method.modifiers, method.selector, null, null, null, method.scope.referenceType().binding)); // } // } // } // } // } }