blob: 642504d341ff9711791b068f078235bbe49e6b97 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist;
import java.util.*;
import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.codeassist.impl.*;
import org.eclipse.jdt.internal.codeassist.select.*;
import org.eclipse.jdt.internal.compiler.*;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.lookup.*;
import org.eclipse.jdt.internal.compiler.parser.*;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.problem.*;
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.util.ASTNodeFinder;
import org.eclipse.jdt.internal.core.util.ElementInfoConverter;
/**
* 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[][][] acceptedClasses;
private char[][][] acceptedInterfaces;
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) {
IProblem pb = super.createProblem(
fileName,
problemId,
problemArguments,
messageArguments,
severity,
problemStartPosition,
problemEndPosition,
lineNumber);
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);
}
/**
* 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,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
this.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;
this.requestor.acceptInterface(
packageName,
interfaceName,
false,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
this.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) {
// implementation of interface method
}
private void acceptQualifiedTypes() {
if(acceptedClasses != null){
this.acceptedAnswer = true;
for (int i = 0; i < acceptedClassesCount; i++) {
noProposal = false;
this.requestor.acceptClass(
acceptedClasses[i][0],
acceptedClasses[i][1],
true,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
}
acceptedClasses = null;
acceptedClassesCount = 0;
}
if(acceptedInterfaces != null){
this.acceptedAnswer = true;
for (int i = 0; i < acceptedInterfacesCount; i++) {
noProposal = false;
this.requestor.acceptInterface(
acceptedInterfaces[i][0],
acceptedInterfaces[i][1],
true,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
}
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 == source.length ? selectionEnd : 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;
}
/*
* 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 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);
}
if (!checkSelection(source, selectionSourceStart, selectionSourceEnd))
return;
try {
this.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) {
if(selectDeclaration(parsedUnit))
return;
lookupEnvironment.buildTypeBindings(parsedUnit);
if ((this.unitScope = parsedUnit.scope) != null) {
try {
lookupEnvironment.completeTypeBindings(parsedUnit, true);
parsedUnit.scope.faultInTypes();
ASTNode 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, 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 (!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, CompilationUnitDeclaration parsedUnit, boolean isDeclaration) {
if (binding instanceof ReferenceBinding) {
ReferenceBinding typeBinding = (ReferenceBinding) binding;
if (qualifiedSelection != null
&& !CharOperation.equals(qualifiedSelection, typeBinding.readableName())) {
return;
}
if (typeBinding.isInterface()) {
noProposal = false;
if (isLocal(typeBinding) && this.requestor instanceof SelectionRequestor) {
((SelectionRequestor)this.requestor).acceptLocalType(
(SourceTypeBinding)typeBinding,
parsedUnit);
} else {
this.requestor.acceptInterface(
typeBinding.qualifiedPackageName(),
typeBinding.qualifiedSourceName(),
false,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
}
} else if(typeBinding instanceof ProblemReferenceBinding){
ReferenceBinding original = ((ProblemReferenceBinding) typeBinding).original;
if(original == null) return;
noProposal = false;
if (isLocal(original) && this.requestor instanceof SelectionRequestor) {
((SelectionRequestor)this.requestor).acceptLocalType(
(SourceTypeBinding)original,
parsedUnit);
} else {
this.requestor.acceptClass(
original.qualifiedPackageName(),
original.qualifiedSourceName(),
false,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
}
} else {
noProposal = false;
if (isLocal(typeBinding) && this.requestor instanceof SelectionRequestor) {
((SelectionRequestor)this.requestor).acceptLocalType(
(SourceTypeBinding)typeBinding,
parsedUnit);
} else {
this.requestor.acceptClass(
typeBinding.qualifiedPackageName(),
typeBinding.qualifiedSourceName(),
false,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
}
}
this.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;
ReferenceBinding declaringClass = methodBinding.declaringClass;
if (isLocal(declaringClass) && this.requestor instanceof SelectionRequestor) {
((SelectionRequestor)this.requestor).acceptLocalMethod(
(SourceTypeBinding)declaringClass,
methodBinding.isConstructor()
? declaringClass.sourceName()
: methodBinding.selector,
parameterPackageNames,
parameterTypeNames,
methodBinding.isConstructor(),
parsedUnit,
isDeclaration,
actualSelectionStart,
actualSelectionEnd);
} else {
this.requestor.acceptMethod(
declaringClass.qualifiedPackageName(),
declaringClass.qualifiedSourceName(),
methodBinding.isConstructor()
? declaringClass.sourceName()
: methodBinding.selector,
parameterPackageNames,
parameterTypeNames,
methodBinding.isConstructor(),
isDeclaration,
actualSelectionStart,
actualSelectionEnd);
}
this.acceptedAnswer = true;
} else
if (binding instanceof FieldBinding) {
FieldBinding fieldBinding = (FieldBinding) binding;
ReferenceBinding declaringClass = fieldBinding.declaringClass;
if (declaringClass != null) { // arraylength
noProposal = false;
if (isLocal(declaringClass) && this.requestor instanceof SelectionRequestor) {
((SelectionRequestor)this.requestor).acceptLocalField(
(SourceTypeBinding)declaringClass,
fieldBinding.name,
parsedUnit);
} else {
this.requestor.acceptField(
declaringClass.qualifiedPackageName(),
declaringClass.qualifiedSourceName(),
fieldBinding.name,
false,
this.actualSelectionStart,
this.actualSelectionEnd);
}
this.acceptedAnswer = true;
}
} else
if (binding instanceof LocalVariableBinding) {
if (this.requestor instanceof SelectionRequestor) {
((SelectionRequestor)this.requestor).acceptLocalVariable(
(LocalVariableBinding)binding,
parsedUnit);
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;
noProposal = false;
requestor.acceptPackage(packageBinding.readableName());
this.acceptedAnswer = true;
} else
if(binding instanceof BaseTypeBinding) {
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 = this.getParser().assistIdentifier();
if (assistIdentifier == null) return;
class Visitor extends ASTVisitor {
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;
}
public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
if (fieldDeclaration.name == assistIdentifier){
throw new SelectionNodeFound(fieldDeclaration.binding);
}
return true;
}
public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
if (localTypeDeclaration.name == assistIdentifier) {
throw new SelectionNodeFound(localTypeDeclaration.binding);
}
return true;
}
public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
if (memberTypeDeclaration.name == assistIdentifier) {
throw new SelectionNodeFound(memberTypeDeclaration.binding);
}
return true;
}
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;
}
public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
if (typeDeclaration.name == assistIdentifier) {
throw new SelectionNodeFound(typeDeclaration.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 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 SourceTypeElementInfo[]
* 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, SourceTypeElementInfo[] topLevelTypes, boolean searchInEnvironment) {
try {
this.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);
if (!(sourceType instanceof SourceTypeElementInfo)) return;
SourceType typeHandle = (SourceType) ((SourceTypeElementInfo)sourceType).getHandle();
CompilationUnitDeclaration parsedUnit =
ElementInfoConverter.buildCompilationUnit(
topLevelTypes,
typeHandle.isAnonymous() || typeHandle.isLocal(),
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
TypeDeclaration typeDecl = new ASTNodeFinder(parsedUnit).findType(typeHandle);
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, parsedUnit, 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 (!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 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 = this.getParser().assistIdentifier();
if (assistIdentifier == null) return false;
ImportReference currentPackage = compilationUnit.currentPackage;
char[] packageName = currentPackage == null ? new char[0] : 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;
}
if(!typeDeclaration.isInterface()) {
this.requestor.acceptClass(
packageName,
qualifiedSourceName,
false,
true,
this.actualSelectionStart,
this.actualSelectionEnd);
} else {
this.requestor.acceptInterface(
packageName,
qualifiedSourceName,
false,
true,
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;
}
this.requestor.acceptField(
packageName,
qualifiedSourceName,
fields[i].name,
true,
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,
method.selector,
null, // SelectionRequestor does not need of parameters type for method declaration
null, // SelectionRequestor does not need of parameters type for method declaration
method.isConstructor(),
true,
this.actualSelectionStart,
this.actualSelectionEnd);
this.noProposal = false;
return true;
}
}
return false;
}
}