/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.jdt.groovy.search;
import java.util.ArrayList;
import java.util.List;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.AnnotationNode;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ConstructorNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.PropertyNode;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.eclipse.jdt.groovy.core.util.GroovyUtils;
public class TypeLookupResult {
/**
* Specifies the kind of match found for this type
*/
public enum TypeConfidence {
/**
* Match is certain E.g., type is explicitly declared on a variable
*/
EXACT,
/**
* Match is potential. E.g., it may be from an interface or from a concrete type, but not possible to tell which one from
* the context
*/
POTENTIAL,
/**
* The type has been inferred from local or global context. E.g., by looking at assignment statements
*/
INFERRED,
/**
* The type has been inferred using less precise means. E.g., from an extending ITypeLookup All
* AbstractSimplifiedTypeLookups return this type confidence.
* <p>
* Furthermore, a type confidence of this will not cause the Inferencing engine to end its lookup. It will continue and try
* to find a more confident type using other lookups.
*/
LOOSELY_INFERRED,
/**
* This is an unknown reference
*/
UNKNOWN;
public static TypeConfidence findLessPrecise(TypeConfidence left, TypeConfidence right) {
return left.isLessThan(right) ? left : right;
}
public boolean isLessThan(TypeConfidence that) {
return this.ordinal() > that.ordinal();
}
public boolean isAtLeast(TypeConfidence that) {
return this.ordinal() <= that.ordinal();
}
}
public final TypeConfidence confidence;
public final ClassNode declaringType;
public final ClassNode type;
/**
* the type should be AnnotatedNode, but in Groovy 1.6.5, this type is not compatible with expression nodes
*/
public final ASTNode declaration;
public final VariableScope scope;
/** Extra Javadoc that should appear in hovers. */
public final String extraDoc;
/** Found through Groovy category or extension. */
public boolean isGroovy;
public AnnotationNode enclosingAnnotation;
/**
* The assignment statement that encloses this expression, or null if there is none.
*/
public BinaryExpression enclosingAssignment;
/**
* @param type the type of the expression being analyzed
* @param declaringType the declaring type of the expression if the expression is a field, method, or type reference
* @param confidence the confidence in this type assertion
* @param the declaration that this node refers to, or null if none (ie- the method, field, class, or property node)
* @param scope the variable scope at this location
*/
public TypeLookupResult(ClassNode type, ClassNode declaringType, ASTNode declaration, TypeConfidence confidence, VariableScope scope) {
this(type, declaringType, declaration, confidence, scope, null);
}
/**
* @param type the type of the expression being analyzed
* @param declaringType the declaring type of the expression if the expression is a field, method, or type reference
* @param confidence the confidence in this type assertion
* @param the declaration that this node refers to, or null if none (ie- the method, field, class, or property node)
* @param scope the variable scope at this location
* @param extraDoc extra javadoc to be shown in hovers
*/
public TypeLookupResult(ClassNode type, ClassNode declaringType, ASTNode declaration, TypeConfidence confidence, VariableScope scope, String extraDoc) {
this.confidence = confidence;
this.type = type;
this.declaringType = declaringType;
this.declaration = declaration;
this.scope = scope;
this.extraDoc = extraDoc;
}
/**
* Replaces type parameters with resolved types.
*/
public TypeLookupResult resolveTypeParameterization(ClassNode objExprType, boolean isStatic) {
if (declaringType != null && (declaration instanceof FieldNode || declaration instanceof PropertyNode ||
declaration instanceof MethodNode && !(declaration instanceof ConstructorNode))) {
ClassNode targetType = objExprType;
if (targetType == null) {
if (isGroovy || !isStatic) {
targetType = scope.getDelegateOrThis();
} else {
targetType = declaringType;
}
}
targetType = GroovyUtils.getWrapperTypeIfPrimitive(targetType);
if (!(declaration instanceof MethodNode)) {
GenericsMapper mapper = GenericsMapper.gatherGenerics(targetType, declaringType.redirect());
ClassNode maybe = VariableScope.resolveTypeParameterization(mapper, VariableScope.clone(type));
if (!maybe.toString(false).equals(type.toString(false))) {
TypeLookupResult result = new TypeLookupResult(maybe, declaringType, declaration, confidence, scope, extraDoc);
result.enclosingAnnotation = enclosingAnnotation;
result.enclosingAssignment = enclosingAssignment;
result.isGroovy = isGroovy;
return result;
}
} else {
List<ClassNode> argumentTypes = scope.getMethodCallArgumentTypes();
if (isGroovy) {
argumentTypes = new ArrayList<ClassNode>();
argumentTypes.add(targetType);
if (scope.getMethodCallArgumentTypes() != null)
argumentTypes.addAll(scope.getMethodCallArgumentTypes());
}
MethodNode method = (MethodNode) declaration;
if (!isStatic && method.getName().equals("getClass") && method.getParameters().length == 0) {
ClassNode classType = VariableScope.clone(method.getReturnType());
classType.getGenericsTypes()[0].setUpperBounds(new ClassNode[] {targetType});
return new TypeLookupResult(classType, method.getDeclaringClass(), method, confidence, scope, extraDoc);
} else {
GenericsMapper mapper = GenericsMapper.gatherGenerics(argumentTypes, targetType, method, scope.getMethodCallGenericsTypes());
method = VariableScope.resolveTypeParameterization(mapper, method);
if (method != declaration) {
TypeLookupResult result = new TypeLookupResult(method.getReturnType(), method.getDeclaringClass(), method, confidence, scope, extraDoc);
result.enclosingAnnotation = enclosingAnnotation;
result.enclosingAssignment = enclosingAssignment;
result.isGroovy = isGroovy;
return result;
}
}
}
}
return this;
}
}