blob: 07fcf36dc8bf2afcc2934edb4bc269b0295409f4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2009 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.ArrayList;
import java.util.HashMap;
import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
public class MissingTypesGuesser extends ASTVisitor {
public static interface GuessedTypeRequestor {
public void accept(
TypeBinding guessedType,
Binding[] missingElements,
int[] missingElementsStarts,
int[] missingElementsEnds,
boolean hasProblems);
}
private static class ResolutionCleaner extends ASTVisitor {
private HashtableOfObjectToInt bitsMap = new HashtableOfObjectToInt();
private boolean firstCall = true;
public ResolutionCleaner(){
super();
}
private void cleanUp(TypeReference typeReference) {
if (this.firstCall) {
this.bitsMap.put(typeReference, typeReference.bits);
} else {
typeReference.bits = this.bitsMap.get(typeReference);
}
typeReference.resolvedType = null;
}
private void cleanUp(ParameterizedSingleTypeReference typeReference) {
this.cleanUp((TypeReference)typeReference);
typeReference.bits &= ~ASTNode.DidResolve;
}
private void cleanUp(ParameterizedQualifiedTypeReference typeReference) {
this.cleanUp((TypeReference)typeReference);
typeReference.bits &= ~ASTNode.DidResolve;
}
public void cleanUp(TypeReference convertedType, BlockScope scope) {
convertedType.traverse(this, scope);
this.firstCall = false;
}
public void cleanUp(TypeReference convertedType, ClassScope scope) {
convertedType.traverse(this, scope);
this.firstCall = false;
}
public boolean visit(SingleTypeReference singleTypeReference, BlockScope scope) {
this.cleanUp(singleTypeReference);
return true;
}
public boolean visit(SingleTypeReference singleTypeReference, ClassScope scope) {
this.cleanUp(singleTypeReference);
return true;
}
public boolean visit(Wildcard wildcard, BlockScope scope) {
this.cleanUp(wildcard);
return true;
}
public boolean visit(Wildcard wildcard, ClassScope scope) {
this.cleanUp(wildcard);
return true;
}
public boolean visit(ArrayTypeReference arrayTypeReference, BlockScope scope) {
this.cleanUp(arrayTypeReference);
return true;
}
public boolean visit(ArrayTypeReference arrayTypeReference, ClassScope scope) {
this.cleanUp(arrayTypeReference);
return true;
}
public boolean visit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, BlockScope scope) {
this.cleanUp(parameterizedSingleTypeReference);
return true;
}
public boolean visit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, ClassScope scope) {
this.cleanUp(parameterizedSingleTypeReference);
return true;
}
public boolean visit(QualifiedTypeReference qualifiedTypeReference, BlockScope scope) {
this.cleanUp(qualifiedTypeReference);
return true;
}
public boolean visit(QualifiedTypeReference qualifiedTypeReference, ClassScope scope) {
this.cleanUp(qualifiedTypeReference);
return true;
}
public boolean visit(ArrayQualifiedTypeReference arrayQualifiedTypeReference, BlockScope scope) {
this.cleanUp(arrayQualifiedTypeReference);
return true;
}
public boolean visit(ArrayQualifiedTypeReference arrayQualifiedTypeReference, ClassScope scope) {
this.cleanUp(arrayQualifiedTypeReference);
return true;
}
public boolean visit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, BlockScope scope) {
this.cleanUp(parameterizedQualifiedTypeReference);
return true;
}
public boolean visit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, ClassScope scope) {
this.cleanUp(parameterizedQualifiedTypeReference);
return true;
}
}
private CompletionEngine.CompletionProblemFactory problemFactory ;
private SearchableEnvironment nameEnvironment;
private HashMap substituedTypes;
private HashMap originalTypes;
private int combinationsCount;
public MissingTypesGuesser(CompletionEngine completionEngine) {
this.problemFactory = completionEngine.problemFactory;
this.nameEnvironment = completionEngine.nameEnvironment;
}
private boolean computeMissingElements(
QualifiedTypeReference[] substituedTypeNodes,
char[][][] originalTypeNames,
Binding[] missingElements,
int[] missingElementsStarts,
int[] missingElementsEnds) {
int length = substituedTypeNodes.length;
for (int i = 0; i < length; i++) {
TypeReference substituedType = substituedTypeNodes[i];
if (substituedType.resolvedType == null) return false;
ReferenceBinding erasure = (ReferenceBinding)substituedType.resolvedType.leafComponentType().erasure();
Binding missingElement;
int depthToRemove = originalTypeNames[i].length - 1 ;
if (depthToRemove == 0) {
missingElement = erasure;
} else {
int depth = erasure.depth() + 1;
if (depth > depthToRemove) {
missingElement = erasure.enclosingTypeAt(depthToRemove);
} else {
return false;
///////////////////////////////////////////////////////////
//// Uncomment the following code to return missing package
///////////////////////////////////////////////////////////
//depthToRemove -= depth;
//PackageBinding packageBinding = erasure.getPackage();
//while(depthToRemove > 0) {
// packageBinding = packageBinding.parent;
// depthToRemove--;
//}
//missingElement = packageBinding;
}
}
missingElements[i] = missingElement;
missingElementsStarts[i] = substituedType.sourceStart;
missingElementsEnds[i] = substituedType.sourceEnd + 1;
}
return true;
}
private TypeReference convert(ArrayQualifiedTypeReference typeRef) {
if (typeRef.resolvedType != null) {
if (typeRef.resolvedType.isValidBinding()) {
ArrayQualifiedTypeReference convertedType =
new ArrayQualifiedTypeReference(
typeRef.tokens,
typeRef.dimensions(),
typeRef.sourcePositions);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
return convertedType;
} else if((typeRef.resolvedType.problemId() & ProblemReasons.NotFound) != 0) {
// only the first token must be resolved
if(((ReferenceBinding)typeRef.resolvedType.leafComponentType()).compoundName.length != 1) return null;
char[][] typeName = typeRef.getTypeName();
char[][][] typeNames = findTypeNames(typeName);
if(typeNames == null || typeNames.length == 0) return null;
ArrayQualifiedTypeReference convertedType =
new ArrayQualifiedTypeReference(
typeNames[0],
typeRef.dimensions(),
new long[typeNames[0].length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = (int)(typeRef.sourcePositions[0] & 0x00000000FFFFFFFFL);
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
return convertedType;
}
}
return null;
}
private TypeReference convert(ArrayTypeReference typeRef) {
if (typeRef.resolvedType != null) {
if (typeRef.resolvedType.isValidBinding()) {
ArrayTypeReference convertedType =
new ArrayTypeReference(
typeRef.token,
typeRef.dimensions,
0);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.originalSourceEnd;
return convertedType;
} else if((typeRef.resolvedType.problemId() & ProblemReasons.NotFound) != 0) {
char[][] typeName = typeRef.getTypeName();
char[][][] typeNames = findTypeNames(typeName);
if(typeNames == null || typeNames.length == 0) return null;
ArrayQualifiedTypeReference convertedType =
new ArrayQualifiedTypeReference(
typeNames[0],
typeRef.dimensions,
new long[typeNames[0].length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.originalSourceEnd;
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
return convertedType;
}
}
return null;
}
private TypeReference convert(ParameterizedQualifiedTypeReference typeRef) {
if (typeRef.resolvedType != null) {
TypeReference[][] typeArguments = typeRef.typeArguments;
int length = typeArguments.length;
TypeReference[][] convertedTypeArguments = new TypeReference[length][];
next : for (int i = 0; i < length; i++) {
if (typeArguments[i] == null) continue next;
int length2 = typeArguments[i].length;
convertedTypeArguments[i] = new TypeReference[length2];
for (int j = 0; j < length2; j++) {
convertedTypeArguments[i][j] = convert(typeArguments[i][j]);
if (convertedTypeArguments[i][j] == null) return null;
}
}
if (typeRef.resolvedType.isValidBinding()) {
ParameterizedQualifiedTypeReference convertedType =
new ParameterizedQualifiedTypeReference(
typeRef.tokens,
convertedTypeArguments,
typeRef.dimensions(),
new long[typeRef.tokens.length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
return convertedType;
} else if((typeRef.resolvedType.problemId() & ProblemReasons.NotFound) != 0) {
// only the first token must be resolved
if(((ReferenceBinding)typeRef.resolvedType.leafComponentType()).compoundName.length != 1) return null;
char[][] typeName = typeRef.getTypeName();
char[][][] typeNames = findTypeNames(typeName);
if(typeNames == null || typeNames.length == 0) return null;
TypeReference[][] newConvertedTypeArguments = new TypeReference[typeNames[0].length][];
for (int k = newConvertedTypeArguments.length - 1, l = convertedTypeArguments.length -1; k > -1 && l > -1;) {
newConvertedTypeArguments[k] = convertedTypeArguments[l];
k--;
l--;
}
ParameterizedQualifiedTypeReference convertedType =
new ParameterizedQualifiedTypeReference(
typeNames[0],
newConvertedTypeArguments,
typeRef.dimensions(),
new long[typeNames[0].length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = (int)(typeRef.sourcePositions[0] & 0x00000000FFFFFFFFL);
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
return convertedType;
}
}
return null;
}
private TypeReference convert(ParameterizedSingleTypeReference typeRef) {
if (typeRef.resolvedType != null) {
TypeReference[] typeArguments = typeRef.typeArguments;
int length = typeArguments.length;
TypeReference[] convertedTypeArguments = new TypeReference[length];
for (int i = 0; i < length; i++) {
convertedTypeArguments[i] = convert(typeArguments[i]);
if(convertedTypeArguments[i] == null) return null;
}
if (typeRef.resolvedType.isValidBinding()) {
ParameterizedSingleTypeReference convertedType =
new ParameterizedSingleTypeReference(
typeRef.token,
convertedTypeArguments,
typeRef.dimensions,
0);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
return convertedType;
} else if((typeRef.resolvedType.problemId() & ProblemReasons.NotFound) != 0) {
char[][] typeName = typeRef.getTypeName();
char[][][] typeNames = findTypeNames(typeName);
if(typeNames == null || typeNames.length == 0) return null;
TypeReference[][] allConvertedTypeArguments = new TypeReference[typeNames[0].length][];
allConvertedTypeArguments[allConvertedTypeArguments.length - 1] = convertedTypeArguments;
ParameterizedQualifiedTypeReference convertedType =
new ParameterizedQualifiedTypeReference(
typeNames[0],
allConvertedTypeArguments,
typeRef.dimensions,
new long[typeNames[0].length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
return convertedType;
}
}
return null;
}
private TypeReference convert(QualifiedTypeReference typeRef) {
if (typeRef.resolvedType != null) {
if (typeRef.resolvedType.isValidBinding()) {
QualifiedTypeReference convertedType = new QualifiedTypeReference(typeRef.tokens, typeRef.sourcePositions);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
return convertedType;
} else if((typeRef.resolvedType.problemId() & ProblemReasons.NotFound) != 0) {
// only the first token must be resolved
if(((ReferenceBinding)typeRef.resolvedType).compoundName.length != 1) return null;
char[][] typeName = typeRef.getTypeName();
char[][][] typeNames = findTypeNames(typeName);
if(typeNames == null || typeNames.length == 0) return null;
QualifiedTypeReference convertedType = new QualifiedTypeReference(typeNames[0], new long[typeNames[0].length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = (int)(typeRef.sourcePositions[0] & 0x00000000FFFFFFFFL);
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
return convertedType;
}
}
return null;
}
private TypeReference convert(SingleTypeReference typeRef) {
if (typeRef.resolvedType != null) {
if (typeRef.resolvedType.isValidBinding()) {
SingleTypeReference convertedType = new SingleTypeReference(typeRef.token, 0);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
return convertedType;
} else if((typeRef.resolvedType.problemId() & ProblemReasons.NotFound) != 0) {
char[][] typeName = typeRef.getTypeName();
char[][][] typeNames = findTypeNames(typeName);
if(typeNames == null || typeNames.length == 0) return null;
QualifiedTypeReference convertedType = new QualifiedTypeReference(typeNames[0], new long[typeNames[0].length]);
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
this.substituedTypes.put(convertedType, typeNames);
this.originalTypes.put(convertedType, typeName);
this.combinationsCount *= typeNames.length;
return convertedType;
}
}
return null;
}
private TypeReference convert(TypeReference typeRef) {
if (typeRef instanceof ParameterizedSingleTypeReference) {
return convert((ParameterizedSingleTypeReference)typeRef);
} else if(typeRef instanceof ParameterizedQualifiedTypeReference) {
return convert((ParameterizedQualifiedTypeReference)typeRef);
} else if (typeRef instanceof ArrayTypeReference) {
return convert((ArrayTypeReference)typeRef);
} else if(typeRef instanceof ArrayQualifiedTypeReference) {
return convert((ArrayQualifiedTypeReference)typeRef);
} else if(typeRef instanceof Wildcard) {
return convert((Wildcard)typeRef);
} else if (typeRef instanceof SingleTypeReference) {
return convert((SingleTypeReference)typeRef);
} else if (typeRef instanceof QualifiedTypeReference) {
return convert((QualifiedTypeReference)typeRef);
}
return null;
}
private TypeReference convert(Wildcard typeRef) {
TypeReference bound = typeRef.bound;
TypeReference convertedBound = null;
if (bound != null) {
convertedBound = convert(bound);
if (convertedBound == null) return null;
}
Wildcard convertedType = new Wildcard(typeRef.kind);
convertedType.bound = convertedBound;
convertedType.sourceStart = typeRef.sourceStart;
convertedType.sourceEnd = typeRef.sourceEnd;
return convertedType;
}
private char[][][] findTypeNames(char[][] missingTypeName) {
char[] missingSimpleName = missingTypeName[missingTypeName.length - 1];
final boolean isQualified = missingTypeName.length > 1;
final char[] missingFullyQualifiedName =
isQualified ? CharOperation.concatWith(missingTypeName, '.') : null;
final ArrayList results = new ArrayList();
ISearchRequestor storage = new ISearchRequestor() {
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 acceptPackage(char[] packageName) {
// package aren't searched
}
public void acceptType(
char[] packageName,
char[] typeName,
char[][] enclosingTypeNames,
int modifiers,
AccessRestriction accessRestriction) {
char[] fullyQualifiedName = CharOperation.concat(packageName, CharOperation.concat(CharOperation.concatWith(enclosingTypeNames, '.'), typeName, '.'), '.');
if (isQualified && !CharOperation.endsWith(fullyQualifiedName, missingFullyQualifiedName)) return;
char[][] compoundName = CharOperation.splitOn('.', fullyQualifiedName);
results.add(compoundName);
}
};
this.nameEnvironment.findExactTypes(missingSimpleName, true, IJavaSearchConstants.TYPE, storage);
if(results.size() == 0) return null;
return (char[][][])results.toArray(new char[results.size()][0][0]);
}
private char[][] getOriginal(TypeReference typeRef) {
return (char[][])this.originalTypes.get(typeRef);
}
private QualifiedTypeReference[] getSubstituedTypes() {
Set types = this.substituedTypes.keySet();
return (QualifiedTypeReference[]) types.toArray(new QualifiedTypeReference[types.size()]);
}
private char[][][] getSubstitution(TypeReference typeRef) {
return (char[][][])this.substituedTypes.get(typeRef);
}
public void guess(TypeReference typeRef, Scope scope, GuessedTypeRequestor requestor) {
this.substituedTypes = new HashMap();
this.originalTypes = new HashMap();
this.combinationsCount = 1;
TypeReference convertedType = convert(typeRef);
if(convertedType == null) return;
QualifiedTypeReference[] substituedTypeNodes = getSubstituedTypes();
int length = substituedTypeNodes.length;
int[] substitutionsIndexes = new int[substituedTypeNodes.length];
char[][][][] subtitutions = new char[substituedTypeNodes.length][][][];
char[][][] originalTypeNames = new char[substituedTypeNodes.length][][];
for (int i = 0; i < substituedTypeNodes.length; i++) {
subtitutions[i] = getSubstitution(substituedTypeNodes[i]);
originalTypeNames[i] = getOriginal(substituedTypeNodes[i]);
}
ResolutionCleaner resolutionCleaner = new ResolutionCleaner();
for (int i = 0; i < this.combinationsCount; i++) {
nextSubstitution(substituedTypeNodes, subtitutions, substitutionsIndexes);
this.problemFactory.startCheckingProblems();
TypeBinding guessedType = null;
switch (scope.kind) {
case Scope.METHOD_SCOPE :
case Scope.BLOCK_SCOPE :
resolutionCleaner.cleanUp(convertedType, (BlockScope)scope);
guessedType = convertedType.resolveType((BlockScope)scope);
break;
case Scope.CLASS_SCOPE :
resolutionCleaner.cleanUp(convertedType, (ClassScope)scope);
guessedType = convertedType.resolveType((ClassScope)scope);
break;
}
this.problemFactory.stopCheckingProblems();
if (!this.problemFactory.hasForbiddenProblems) {
if (guessedType != null) {
Binding[] missingElements = new Binding[length];
int[] missingElementsStarts = new int[length];
int[] missingElementsEnds = new int[length];
if(computeMissingElements(
substituedTypeNodes,
originalTypeNames,
missingElements,
missingElementsStarts,
missingElementsEnds)) {
requestor.accept(
guessedType.capture(scope, typeRef.sourceEnd),
missingElements,
missingElementsStarts,
missingElementsEnds,
this.problemFactory.hasAllowedProblems);
}
}
}
}
}
private void nextSubstitution(
QualifiedTypeReference[] substituedTypeNodes,
char[][][][] subtitutions,
int[] substitutionsIndexes) {
int length = substituedTypeNodes.length;
done : for (int i = 0; i < length; i++) {
if(substitutionsIndexes[i] < subtitutions[i].length - 1) {
substitutionsIndexes[i]++;
break done;
} else {
substitutionsIndexes[i] = 0;
}
}
for (int i = 0; i < length; i++) {
QualifiedTypeReference qualifiedTypeReference = substituedTypeNodes[i];
qualifiedTypeReference.tokens = subtitutions[i][substitutionsIndexes[i]];
qualifiedTypeReference.sourcePositions = new long[qualifiedTypeReference.tokens.length];
if(qualifiedTypeReference instanceof ParameterizedQualifiedTypeReference) {
ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference =
(ParameterizedQualifiedTypeReference)qualifiedTypeReference;
TypeReference[][] typeArguments = parameterizedQualifiedTypeReference.typeArguments;
TypeReference[][] newTypeArguments = new TypeReference[qualifiedTypeReference.tokens.length][];
for (int j = newTypeArguments.length - 1, k = typeArguments.length -1; j > -1 && k > -1;) {
newTypeArguments[j] = typeArguments[k];
j--;
k--;
}
}
}
}
}