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 }