Refactor JavaConstructorResolver

Extract methods, fix warnings, use JavaClass/JavaMethod instead of Psi
This commit is contained in:
Alexander Udalov
2013-07-30 22:03:03 +04:00
parent 62868421cf
commit 998e598846
2 changed files with 93 additions and 72 deletions
@@ -17,7 +17,8 @@
package org.jetbrains.jet.lang.resolve.java.resolver;
import com.google.common.collect.Lists;
import com.intellij.psi.*;
import com.intellij.psi.PsiArrayType;
import com.intellij.psi.PsiType;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -28,16 +29,19 @@ import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.DescriptorResolver;
import org.jetbrains.jet.lang.resolve.java.*;
import org.jetbrains.jet.lang.resolve.java.JavaBindingContext;
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
import org.jetbrains.jet.lang.resolve.java.JavaVisibilities;
import org.jetbrains.jet.lang.resolve.java.TypeVariableResolver;
import org.jetbrains.jet.lang.resolve.java.kotlinSignature.AlternativeMethodSignatureData;
import org.jetbrains.jet.lang.resolve.java.structure.JavaClass;
import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import static org.jetbrains.jet.lang.resolve.java.resolver.JavaFunctionResolver.recordSamAdapter;
@@ -69,83 +73,15 @@ public final class JavaConstructorResolver {
@NotNull
public Collection<ConstructorDescriptor> resolveConstructors(@NotNull JavaClass javaClass, @NotNull ClassDescriptor containingClass) {
PsiClass psiClass = javaClass.getPsi();
Collection<ConstructorDescriptor> result = Lists.newArrayList();
List<TypeParameterDescriptor> typeParameters = containingClass.getTypeConstructor().getParameters();
TypeVariableResolver typeVariableResolver =
new TypeVariableResolver(typeParameters, containingClass, "class " + javaClass.getFqName());
Collection<JavaMethod> constructors = javaClass.getConstructors();
if (containingClass.getKind() == ClassKind.OBJECT || containingClass.getKind() == ClassKind.CLASS_OBJECT) {
result.add(DescriptorResolver.createPrimaryConstructorForObject(containingClass));
}
else if (constructors.isEmpty()) {
if (trace.get(BindingContext.CONSTRUCTOR, psiClass) != null) {
result.add(trace.get(BindingContext.CONSTRUCTOR, psiClass));
}
else {
Visibility constructorVisibility = getConstructorVisibility(containingClass);
// We need to create default constructors for classes and abstract classes.
// Example:
// class Kotlin() : Java() {}
// abstract public class Java {}
if (!javaClass.isInterface()) {
ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
containingClass,
Collections.<AnnotationDescriptor>emptyList(),
true);
constructorDescriptor
.initialize(typeParameters, Collections.<ValueParameterDescriptor>emptyList(), constructorVisibility,
javaClass.isStatic());
result.add(constructorDescriptor);
trace.record(BindingContext.CONSTRUCTOR, psiClass, constructorDescriptor);
}
if (javaClass.isAnnotationType()) {
// A constructor for an annotation type takes all the "methods" in the @interface as parameters
ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
containingClass,
Collections.<AnnotationDescriptor>emptyList(),
true);
List<ValueParameterDescriptor> valueParameters = Lists.newArrayList();
PsiMethod[] methods = psiClass.getMethods();
for (int i = 0; i < methods.length; i++) {
PsiMethod method = methods[i];
if (method instanceof PsiAnnotationMethod) {
PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod) method;
assert annotationMethod.getParameterList().getParameters().length == 0;
PsiType returnType = annotationMethod.getReturnType();
// We take the following heuristical convention:
// if the last method of the @interface is an array, we convert it into a vararg
JetType varargElementType = null;
if (i == methods.length - 1 && (returnType instanceof PsiArrayType)) {
varargElementType = typeTransformer
.transformToType(((PsiArrayType) returnType).getComponentType(), typeVariableResolver);
}
assert returnType != null;
valueParameters.add(new ValueParameterDescriptorImpl(
constructorDescriptor,
i,
Collections.<AnnotationDescriptor>emptyList(),
Name.identifier(method.getName()),
typeTransformer.transformToType(returnType, typeVariableResolver),
annotationMethod.getDefaultValue() != null,
varargElementType));
}
}
constructorDescriptor.initialize(typeParameters, valueParameters, constructorVisibility, javaClass.isStatic());
result.add(constructorDescriptor);
trace.record(BindingContext.CONSTRUCTOR, psiClass, constructorDescriptor);
}
}
ContainerUtil.addIfNotNull(result, resolveDefaultConstructor(javaClass, containingClass));
}
else {
for (JavaMethod constructor : constructors) {
@@ -162,6 +98,81 @@ public final class JavaConstructorResolver {
return result;
}
@Nullable
private ConstructorDescriptor resolveDefaultConstructor(@NotNull JavaClass javaClass, @NotNull ClassDescriptor containingClass) {
ConstructorDescriptor alreadyResolved = trace.get(BindingContext.CONSTRUCTOR, javaClass.getPsi());
if (alreadyResolved != null) {
return alreadyResolved;
}
boolean isAnnotation = javaClass.isAnnotationType();
if (javaClass.isInterface() && !isAnnotation) return null;
ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
containingClass,
Collections.<AnnotationDescriptor>emptyList(),
true);
List<TypeParameterDescriptor> typeParameters = containingClass.getTypeConstructor().getParameters();
List<ValueParameterDescriptor> valueParameters;
if (isAnnotation) {
TypeVariableResolver typeVariableResolver =
new TypeVariableResolver(typeParameters, containingClass, "class " + javaClass.getFqName());
valueParameters = resolveAnnotationParameters(javaClass, constructorDescriptor, typeVariableResolver);
}
else {
valueParameters = Collections.emptyList();
}
constructorDescriptor.initialize(typeParameters, valueParameters, getConstructorVisibility(containingClass), javaClass.isStatic());
trace.record(BindingContext.CONSTRUCTOR, javaClass.getPsi(), constructorDescriptor);
return constructorDescriptor;
}
@NotNull
private List<ValueParameterDescriptor> resolveAnnotationParameters(
@NotNull JavaClass javaClass,
@NotNull ConstructorDescriptor constructorDescriptor,
@NotNull TypeVariableResolver typeVariableResolver
) {
// A constructor for an annotation type takes all the "methods" in the @interface as parameters
List<ValueParameterDescriptor> result = Lists.newArrayList();
int index = 0;
for (Iterator<JavaMethod> iterator = javaClass.getMethods().iterator(); iterator.hasNext(); ) {
JavaMethod method = iterator.next();
assert method.getValueParameters().isEmpty() : "Annotation method can't have parameters: " + method;
PsiType returnType = method.getPsi().getReturnType();
assert returnType != null : "Annotation method has no return type: " + method;
// We take the following heuristic convention:
// if the last method of the @interface is an array, we convert it into a vararg
JetType varargElementType = null;
if (!iterator.hasNext() && returnType instanceof PsiArrayType) {
PsiType componentType = ((PsiArrayType) returnType).getComponentType();
varargElementType = typeTransformer.transformToType(componentType, typeVariableResolver);
}
result.add(new ValueParameterDescriptorImpl(
constructorDescriptor,
index,
Collections.<AnnotationDescriptor>emptyList(),
method.getName(),
typeTransformer.transformToType(returnType, typeVariableResolver),
method.getAnnotationParameterDefaultValue() != null,
varargElementType));
index++;
}
return result;
}
@NotNull
private static Visibility getConstructorVisibility(@NotNull ClassDescriptor classDescriptor) {
Visibility visibility = classDescriptor.getVisibility();
@@ -16,6 +16,7 @@
package org.jetbrains.jet.lang.resolve.java.structure;
import com.intellij.psi.PsiAnnotationMethod;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.NotNull;
@@ -62,4 +63,13 @@ public class JavaMethod extends JavaMemberImpl implements JavaTypeParameterListO
public Collection<JavaValueParameter> getValueParameters() {
return valueParameters(getPsi().getParameterList().getParameters());
}
@Nullable
public Object getAnnotationParameterDefaultValue() {
PsiMethod psiMethod = getPsi();
if (psiMethod instanceof PsiAnnotationMethod) {
return ((PsiAnnotationMethod) psiMethod).getDefaultValue();
}
return null;
}
}