/*******************************************************************************
* 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.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
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--;
}
}
}
}
}