| /******************************************************************************* |
| * Copyright (c) 2000, 2010 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.jdt.internal.codeassist; |
| |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.WorkingCopyOwner; |
| import org.eclipse.jdt.core.compiler.*; |
| 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.*; |
| import org.eclipse.jdt.internal.codeassist.select.*; |
| import org.eclipse.jdt.internal.compiler.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; |
| 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.problem.*; |
| 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.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.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. |
| */ |
| 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; |
| } |
| |
| 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())) { |
| |
| 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; |
| } |
| |
| 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 |
| } |
| |
| 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; |
| } |
| } |
| } |
| |
| /** |
| * 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(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) { |
| |
| Scanner scanner = |
| new Scanner( |
| false /*comment*/, |
| false /*whitespace*/, |
| false /*nls*/, |
| this.compilerOptions.sourceLevel, |
| this.compilerOptions.complianceLevel, |
| null/*taskTag*/, |
| null/*taskPriorities*/, |
| true /*taskCaseSensitive*/); |
| 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.getNumericValue(source[pos++])) > 15 |
| || c1 < 0 |
| || (c2 = ScannerHelper.getNumericValue(source[pos++])) > 15 |
| || c2 < 0 |
| || (c3 = ScannerHelper.getNumericValue(source[pos++])) > 15 |
| || c3 < 0 |
| || (c4 = ScannerHelper.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; |
| } |
| |
| switch(currentCharacter) { |
| case '\r': |
| case '\n': |
| case '/': |
| case '"': |
| case '\'': |
| break lineLoop; |
| } |
| currentPosition--; |
| } |
| } catch (ArrayIndexOutOfBoundsException e) { |
| return false; |
| } |
| |
| // compute start and end of the last token |
| scanner.resetTo(nextCharacterPosition, end); |
| isolateLastName: do { |
| try { |
| token = scanner.getNextToken(); |
| } catch (InvalidInputException e) { |
| return false; |
| } |
| switch (token) { |
| case TerminalTokens.TokenNamethis: |
| case TerminalTokens.TokenNamesuper: |
| 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; |
| } |
| } 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); |
| } |
| expectingIdentifier = false; |
| break; |
| 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; |
| 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; |
| public void beginTask(String name, int totalWork) { |
| // implements interface method |
| } |
| public void done() { |
| // implements interface method |
| } |
| public void internalWorked(double work) { |
| // implements interface method |
| } |
| public boolean isCanceled() { |
| return this.isCanceled; |
| } |
| public void setCanceled(boolean value) { |
| this.isCanceled = value; |
| } |
| public void setTaskName(String name) { |
| // implements interface method |
| } |
| public void subTask(String name) { |
| // implements interface method |
| } |
| public void worked(int work) { |
| // implements interface method |
| } |
| }; |
| |
| TypeNameMatchRequestor typeNameMatchRequestor = new TypeNameMatchRequestor() { |
| 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 |
| } |
| } |
| |
| 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); |
| } |
| if (!checkSelection(source, selectionSourceStart, selectionSourceEnd)) { |
| 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; |
| } |
| } |
| } |
| if (parsedUnit.types != null || parsedUnit.isPackageInfo()) { |
| if(selectDeclaration(parsedUnit)) |
| return; |
| this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); |
| if ((this.unitScope = parsedUnit.scope) != null) { |
| try { |
| 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, 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 e) { // work-around internal failure - 1GEMF6D |
| if(DEBUG) { |
| System.out.println("Exception caught by SelectionEngine:"); //$NON-NLS-1$ |
| e.printStackTrace(System.out); |
| } |
| } catch (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 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) { |
| 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 { |
| 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 = (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 (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); |
| 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; |
| } |
| } |
| /* |
| * 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 { |
| 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; |
| } |
| public boolean visit(TypeParameter typeParameter, BlockScope scope) { |
| if (typeParameter.name == assistIdentifier) { |
| throw new SelectionNodeFound(typeParameter.binding); |
| } |
| return true; |
| } |
| 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); |
| } |
| |
| // find the outer most type |
| IType outerType = context; |
| IType parent = context.getDeclaringType(); |
| while (parent != null) { |
| outerType = parent; |
| parent = parent.getDeclaringType(); |
| } |
| |
| // compute parse tree for this most outer type |
| 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(); |
| } |
| ISourceType outerTypeInfo = (ISourceType) ((SourceType) outerType).getElementInfo(); |
| CompilationResult result = new CompilationResult(outerTypeInfo.getFileName(), 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 |
| typeDeclaration = new ASTNodeFinder(parsedUnit).findType(context); |
| } |
| } else { // binary type |
| ClassFile classFile = (ClassFile) context.getClassFile(); |
| ClassFileReader reader = (ClassFileReader) classFile.getBinaryTypeInfo((IFile) classFile.resource(), false/*don't fully initialize so as to keep constant pool (used below)*/); |
| 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; |
| } |
| } |