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    
017    package org.jetbrains.jet.lang.resolve.java.resolver;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.*;
022    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
023    import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
024    import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
025    import org.jetbrains.jet.lang.resolve.DescriptorResolver;
026    import org.jetbrains.jet.lang.resolve.java.JavaVisibilities;
027    import org.jetbrains.jet.lang.resolve.java.structure.JavaArrayType;
028    import org.jetbrains.jet.lang.resolve.java.structure.JavaClass;
029    import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
030    import org.jetbrains.jet.lang.resolve.java.structure.JavaType;
031    import org.jetbrains.jet.lang.types.JetType;
032    
033    import javax.inject.Inject;
034    import java.util.*;
035    
036    import static org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils.createSamAdapterConstructor;
037    import static org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils.isSamAdapterNecessary;
038    
039    public final class JavaConstructorResolver {
040        private JavaResolverCache cache;
041        private JavaTypeTransformer typeTransformer;
042        private JavaValueParameterResolver valueParameterResolver;
043        private ExternalSignatureResolver externalSignatureResolver;
044    
045        public JavaConstructorResolver() {
046        }
047    
048        @Inject
049        public void setCache(JavaResolverCache cache) {
050            this.cache = cache;
051        }
052    
053        @Inject
054        public void setTypeTransformer(JavaTypeTransformer typeTransformer) {
055            this.typeTransformer = typeTransformer;
056        }
057    
058        @Inject
059        public void setValueParameterResolver(JavaValueParameterResolver valueParameterResolver) {
060            this.valueParameterResolver = valueParameterResolver;
061        }
062    
063        @Inject
064        public void setExternalSignatureResolver(ExternalSignatureResolver externalSignatureResolver) {
065            this.externalSignatureResolver = externalSignatureResolver;
066        }
067    
068        @NotNull
069        public Collection<ConstructorDescriptor> resolveConstructors(@NotNull JavaClass javaClass, @NotNull ClassDescriptor containingClass) {
070            Collection<ConstructorDescriptor> result = new ArrayList<ConstructorDescriptor>();
071    
072            Collection<JavaMethod> constructors = javaClass.getConstructors();
073    
074            if (containingClass.getKind() == ClassKind.OBJECT || containingClass.getKind() == ClassKind.CLASS_OBJECT) {
075                result.add(DescriptorResolver.createPrimaryConstructorForObject(containingClass));
076            }
077            else if (constructors.isEmpty()) {
078                ConstructorDescriptor defaultConstructor = resolveDefaultConstructor(javaClass, containingClass);
079                if (defaultConstructor != null) {
080                    result.add(defaultConstructor);
081                }
082            }
083            else {
084                for (JavaMethod constructor : constructors) {
085                    ConstructorDescriptor descriptor = resolveConstructor(constructor, containingClass, javaClass.isStatic());
086                    result.add(descriptor);
087                    ConstructorDescriptor samAdapter = resolveSamAdapter(descriptor);
088                    if (samAdapter != null) {
089                        result.add(samAdapter);
090                    }
091                }
092            }
093    
094            for (ConstructorDescriptor constructor : result) {
095                ((ConstructorDescriptorImpl) constructor).setReturnType(containingClass.getDefaultType());
096            }
097    
098            return result;
099        }
100    
101        @Nullable
102        private ConstructorDescriptor resolveDefaultConstructor(@NotNull JavaClass javaClass, @NotNull ClassDescriptor containingClass) {
103            ConstructorDescriptor alreadyResolved = cache.getConstructor(javaClass);
104            if (alreadyResolved != null) {
105                return alreadyResolved;
106            }
107    
108            boolean isAnnotation = javaClass.isAnnotationType();
109    
110            if (javaClass.isInterface() && !isAnnotation) return null;
111    
112            ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
113                    containingClass,
114                    Collections.<AnnotationDescriptor>emptyList(),
115                    true);
116    
117            List<TypeParameterDescriptor> typeParameters = containingClass.getTypeConstructor().getParameters();
118    
119            List<ValueParameterDescriptor> valueParameters;
120            if (isAnnotation) {
121                TypeVariableResolver typeVariableResolver = new TypeVariableResolverImpl(typeParameters, containingClass);
122                valueParameters = resolveAnnotationParameters(javaClass, constructorDescriptor, typeVariableResolver);
123            }
124            else {
125                valueParameters = Collections.emptyList();
126            }
127    
128            constructorDescriptor.initialize(typeParameters, valueParameters, getConstructorVisibility(containingClass), javaClass.isStatic());
129    
130            cache.recordConstructor(javaClass, constructorDescriptor);
131    
132            return constructorDescriptor;
133        }
134    
135        @NotNull
136        private List<ValueParameterDescriptor> resolveAnnotationParameters(
137                @NotNull JavaClass javaClass,
138                @NotNull ConstructorDescriptor constructorDescriptor,
139                @NotNull TypeVariableResolver typeVariableResolver
140        ) {
141            // A constructor for an annotation type takes all the "methods" in the @interface as parameters
142            Collection<JavaMethod> methods = javaClass.getMethods();
143            List<ValueParameterDescriptor> result = new ArrayList<ValueParameterDescriptor>(methods.size());
144    
145            int index = 0;
146            for (Iterator<JavaMethod> iterator = methods.iterator(); iterator.hasNext(); ) {
147                JavaMethod method = iterator.next();
148                assert method.getValueParameters().isEmpty() : "Annotation method can't have parameters: " + method;
149    
150                JavaType returnType = method.getReturnType();
151                assert returnType != null : "Annotation method has no return type: " + method;
152    
153                // We take the following heuristic convention:
154                // if the last method of the @interface is an array, we convert it into a vararg
155                JetType varargElementType = null;
156                if (!iterator.hasNext() && returnType instanceof JavaArrayType) {
157                    JavaType componentType = ((JavaArrayType) returnType).getComponentType();
158                    varargElementType = typeTransformer.transformToType(componentType, typeVariableResolver);
159                }
160    
161                result.add(new ValueParameterDescriptorImpl(
162                        constructorDescriptor,
163                        index,
164                        Collections.<AnnotationDescriptor>emptyList(),
165                        method.getName(),
166                        typeTransformer.transformToType(returnType, typeVariableResolver),
167                        method.hasAnnotationParameterDefaultValue(),
168                        varargElementType));
169    
170                index++;
171            }
172    
173            return result;
174        }
175    
176        @NotNull
177        private static Visibility getConstructorVisibility(@NotNull ClassDescriptor classDescriptor) {
178            Visibility visibility = classDescriptor.getVisibility();
179            if (visibility == JavaVisibilities.PROTECTED_STATIC_VISIBILITY) {
180                return JavaVisibilities.PROTECTED_AND_PACKAGE;
181            }
182            return visibility;
183        }
184    
185        @NotNull
186        private ConstructorDescriptor resolveConstructor(
187                @NotNull JavaMethod constructor,
188                @NotNull ClassDescriptor classDescriptor,
189                boolean isStaticClass
190        ) {
191            ConstructorDescriptor alreadyResolved = cache.getConstructor(constructor);
192            if (alreadyResolved != null) {
193                return alreadyResolved;
194            }
195    
196            ConstructorDescriptorImpl constructorDescriptor = new ConstructorDescriptorImpl(
197                    classDescriptor,
198                    Collections.<AnnotationDescriptor>emptyList(), // TODO
199                    false);
200    
201            List<TypeParameterDescriptor> typeParameters = classDescriptor.getTypeConstructor().getParameters();
202    
203            List<ValueParameterDescriptor> valueParameters = valueParameterResolver.resolveValueParameters(
204                    constructorDescriptor, constructor,
205                    new TypeVariableResolverImpl(typeParameters, classDescriptor)
206            );
207    
208            ExternalSignatureResolver.AlternativeMethodSignature effectiveSignature = externalSignatureResolver
209                    .resolveAlternativeMethodSignature(constructor, false, null, null, valueParameters,
210                                                       Collections.<TypeParameterDescriptor>emptyList());
211    
212            constructorDescriptor
213                    .initialize(typeParameters, effectiveSignature.getValueParameters(), constructor.getVisibility(), isStaticClass);
214    
215            List<String> signatureErrors = effectiveSignature.getErrors();
216            if (!signatureErrors.isEmpty()) {
217                externalSignatureResolver.reportSignatureErrors(constructorDescriptor, signatureErrors);
218            }
219    
220            cache.recordConstructor(constructor, constructorDescriptor);
221            return constructorDescriptor;
222        }
223    
224        @Nullable
225        private static ConstructorDescriptor resolveSamAdapter(@NotNull ConstructorDescriptor original) {
226            return isSamAdapterNecessary(original) ? (ConstructorDescriptor) createSamAdapterConstructor(original) : null;
227        }
228    }