001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.jet.lang.resolve.java.resolver;
018
019import com.google.common.collect.Lists;
020import com.intellij.psi.*;
021import com.intellij.util.containers.ContainerUtil;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.annotations.Nullable;
024import org.jetbrains.jet.lang.descriptors.*;
025import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
027import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
028import org.jetbrains.jet.lang.resolve.BindingContext;
029import org.jetbrains.jet.lang.resolve.BindingTrace;
030import org.jetbrains.jet.lang.resolve.DescriptorResolver;
031import org.jetbrains.jet.lang.resolve.java.*;
032import org.jetbrains.jet.lang.resolve.java.kotlinSignature.AlternativeMethodSignatureData;
033import org.jetbrains.jet.lang.resolve.java.kt.JetConstructorAnnotation;
034import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
035import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
036import org.jetbrains.jet.lang.resolve.name.Name;
037import org.jetbrains.jet.lang.types.JetType;
038
039import javax.inject.Inject;
040import java.util.Collection;
041import java.util.Collections;
042import java.util.List;
043
044import static org.jetbrains.jet.lang.resolve.java.resolver.JavaFunctionResolver.recordSamAdapter;
045import static org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils.createSamAdapterConstructor;
046import static org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils.isSamAdapterNecessary;
047
048public final class JavaConstructorResolver {
049    private BindingTrace trace;
050    private JavaTypeTransformer typeTransformer;
051    private JavaValueParameterResolver valueParameterResolver;
052
053    public JavaConstructorResolver() {
054    }
055
056    @Inject
057    public void setTrace(BindingTrace trace) {
058        this.trace = trace;
059    }
060
061    @Inject
062    public void setTypeTransformer(JavaTypeTransformer typeTransformer) {
063        this.typeTransformer = typeTransformer;
064    }
065
066    @Inject
067    public void setValueParameterResolver(JavaValueParameterResolver valueParameterResolver) {
068        this.valueParameterResolver = valueParameterResolver;
069    }
070
071    @NotNull
072    public Collection<ConstructorDescriptor> resolveConstructors(
073            @NotNull ClassPsiDeclarationProvider classData, @NotNull ClassDescriptor containingClass
074    ) {
075        Collection<ConstructorDescriptor> constructors = Lists.newArrayList();
076
077        PsiClass psiClass = classData.getPsiClass();
078
079        TypeVariableResolver resolverForTypeParameters = TypeVariableResolvers.classTypeVariableResolver(
080                containingClass, "class " + psiClass.getQualifiedName());
081
082        List<TypeParameterDescriptor> typeParameters = containingClass.getTypeConstructor().getParameters();
083
084        PsiMethod[] psiConstructors = psiClass.getConstructors();
085
086        boolean isStatic = psiClass.hasModifierProperty(PsiModifier.STATIC);
087        if (containingClass.getKind() == ClassKind.OBJECT || containingClass.getKind() == ClassKind.CLASS_OBJECT) {
088            constructors.add(DescriptorResolver.createPrimaryConstructorForObject(containingClass));
089        }
090        else if (psiConstructors.length == 0) {
091            if (trace.get(BindingContext.CONSTRUCTOR, psiClass) != null) {
092                constructors.add(trace.get(BindingContext.CONSTRUCTOR, psiClass));
093            }
094            else {
095                Visibility constructorVisibility = DescriptorResolverUtils.getConstructorVisibility(containingClass);
096                // We need to create default constructors for classes and abstract classes.
097                // Example:
098                // class Kotlin() : Java() {}
099                // abstract public class Java {}
100                if (!psiClass.isInterface()) {
101                    ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
102                            containingClass,
103                            Collections.<AnnotationDescriptor>emptyList(),
104                            true);
105                    constructorDescriptor.initialize(typeParameters, Collections.<ValueParameterDescriptor>emptyList(), constructorVisibility, isStatic);
106                    constructors.add(constructorDescriptor);
107                    trace.record(BindingContext.CONSTRUCTOR, psiClass, constructorDescriptor);
108                }
109                if (psiClass.isAnnotationType()) {
110                    // A constructor for an annotation type takes all the "methods" in the @interface as parameters
111                    ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
112                            containingClass,
113                            Collections.<AnnotationDescriptor>emptyList(),
114                            true);
115
116                    List<ValueParameterDescriptor> valueParameters = Lists.newArrayList();
117                    PsiMethod[] methods = psiClass.getMethods();
118                    for (int i = 0; i < methods.length; i++) {
119                        PsiMethod method = methods[i];
120                        if (method instanceof PsiAnnotationMethod) {
121                            PsiAnnotationMethod annotationMethod = (PsiAnnotationMethod) method;
122                            assert annotationMethod.getParameterList().getParameters().length == 0;
123
124                            PsiType returnType = annotationMethod.getReturnType();
125
126                            // We take the following heuristical convention:
127                            // if the last method of the @interface is an array, we convert it into a vararg
128                            JetType varargElementType = null;
129                            if (i == methods.length - 1 && (returnType instanceof PsiArrayType)) {
130                                varargElementType = typeTransformer
131                                        .transformToType(((PsiArrayType) returnType).getComponentType(), resolverForTypeParameters);
132                            }
133
134                            assert returnType != null;
135                            valueParameters.add(new ValueParameterDescriptorImpl(
136                                    constructorDescriptor,
137                                    i,
138                                    Collections.<AnnotationDescriptor>emptyList(),
139                                    Name.identifier(method.getName()),
140                                    typeTransformer.transformToType(returnType, resolverForTypeParameters),
141                                    annotationMethod.getDefaultValue() != null,
142                                    varargElementType));
143                        }
144                    }
145
146                    constructorDescriptor.initialize(typeParameters, valueParameters, constructorVisibility, isStatic);
147                    constructors.add(constructorDescriptor);
148                    trace.record(BindingContext.CONSTRUCTOR, psiClass, constructorDescriptor);
149                }
150            }
151        }
152        else {
153            for (PsiMethod psiConstructor : psiConstructors) {
154                ConstructorDescriptor constructor = resolveConstructor(psiClass, isStatic, psiConstructor, containingClass);
155                if (constructor != null) {
156                    constructors.add(constructor);
157                    ContainerUtil.addIfNotNull(constructors, resolveSamAdapter(constructor));
158                }
159            }
160        }
161
162        for (ConstructorDescriptor constructor : constructors) {
163            ((ConstructorDescriptorImpl) constructor).setReturnType(containingClass.getDefaultType());
164        }
165
166        return constructors;
167    }
168
169    @Nullable
170    private ConstructorDescriptor resolveConstructor(
171            PsiClass psiClass,
172            boolean aStatic,
173            PsiMethod psiConstructor,
174            ClassDescriptor classDescriptor
175    ) {
176        PsiMethodWrapper constructor = new PsiMethodWrapper(psiConstructor);
177
178        JetConstructorAnnotation constructorAnnotation = constructor.getJetConstructorAnnotation();
179        //noinspection deprecation
180        if (constructorAnnotation.hidden()) {
181            return null;
182        }
183
184        // Do not resolve kotlin constructors without JetConstructorAnnotation
185        if (DescriptorResolverUtils.isKotlinClass(psiClass) && !constructorAnnotation.isDefined()) {
186            return null;
187        }
188
189        if (trace.get(BindingContext.CONSTRUCTOR, psiConstructor) != null) {
190            return trace.get(BindingContext.CONSTRUCTOR, psiConstructor);
191        }
192
193        boolean primary = constructor.getJetConstructorAnnotation().isDefined();
194
195        ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
196                classDescriptor,
197                Collections.<AnnotationDescriptor>emptyList(), // TODO
198                primary);
199
200        String context = "constructor of class " + psiClass.getQualifiedName();
201        JavaDescriptorResolver.ValueParameterDescriptors valueParameterDescriptors = valueParameterResolver.resolveParameterDescriptors(
202                constructorDescriptor, constructor.getParameters(),
203                TypeVariableResolvers.classTypeVariableResolver(classDescriptor, context));
204
205        if (valueParameterDescriptors.getReceiverType() != null) {
206            throw new IllegalStateException();
207        }
208
209        AlternativeMethodSignatureData alternativeMethodSignatureData =
210                new AlternativeMethodSignatureData(constructor, valueParameterDescriptors, null,
211                                                   Collections.<TypeParameterDescriptor>emptyList(), false);
212        if (alternativeMethodSignatureData.isAnnotated() && !alternativeMethodSignatureData.hasErrors()) {
213            valueParameterDescriptors = alternativeMethodSignatureData.getValueParameters();
214        }
215        else if (alternativeMethodSignatureData.hasErrors()) {
216            trace.record(JavaBindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, constructorDescriptor,
217                         Collections.singletonList(alternativeMethodSignatureData.getError()));
218        }
219
220        constructorDescriptor.initialize(classDescriptor.getTypeConstructor().getParameters(),
221                                         valueParameterDescriptors.getDescriptors(),
222                                         DescriptorResolverUtils.resolveVisibility(psiConstructor, constructorAnnotation),
223                                         aStatic);
224        trace.record(BindingContext.CONSTRUCTOR, psiConstructor, constructorDescriptor);
225        return constructorDescriptor;
226    }
227
228    @Nullable
229    private ConstructorDescriptor resolveSamAdapter(@NotNull ConstructorDescriptor original) {
230        return isSamAdapterNecessary(original)
231               ? recordSamAdapter(original, createSamAdapterConstructor(original), trace)
232               : null;
233    }
234}