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