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 }