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;
018
019 import com.google.common.collect.Lists;
020 import com.google.common.collect.Sets;
021 import com.intellij.openapi.diagnostic.Logger;
022 import com.intellij.psi.*;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
026 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027 import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver;
028 import org.jetbrains.jet.lang.resolve.name.FqName;
029 import org.jetbrains.jet.lang.types.*;
030 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
031 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
032 import org.jetbrains.jet.rt.signature.JetSignatureReader;
033
034 import javax.inject.Inject;
035 import java.util.Collections;
036 import java.util.EnumSet;
037 import java.util.List;
038 import java.util.Set;
039
040 import static org.jetbrains.jet.lang.resolve.java.TypeUsage.*;
041 import static org.jetbrains.jet.lang.types.Variance.*;
042
043 public class JavaTypeTransformer {
044
045 private static final Logger LOG = Logger.getInstance(JavaTypeTransformer.class);
046
047 private JavaSemanticServices javaSemanticServices;
048 private JavaDescriptorResolver resolver;
049 @Inject
050 public void setJavaSemanticServices(JavaSemanticServices javaSemanticServices) {
051 this.javaSemanticServices = javaSemanticServices;
052 }
053
054 @Inject
055 public void setResolver(JavaDescriptorResolver resolver) {
056 this.resolver = resolver;
057 }
058
059 @NotNull
060 private TypeProjection transformToTypeProjection(@NotNull PsiType javaType,
061 @NotNull final TypeParameterDescriptor typeParameterDescriptor,
062 @NotNull final TypeVariableResolver typeVariableByPsiResolver,
063 @NotNull final TypeUsage howThisTypeIsUsed
064 ) {
065 TypeProjection result = javaType.accept(new PsiTypeVisitor<TypeProjection>() {
066
067 @Override
068 public TypeProjection visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
069 throw new UnsupportedOperationException(); // TODO
070 }
071
072 @Override
073 public TypeProjection visitWildcardType(PsiWildcardType wildcardType) {
074 if (!wildcardType.isBounded()) {
075 return SubstitutionUtils.makeStarProjection(typeParameterDescriptor);
076 }
077 Variance variance = wildcardType.isExtends() ? OUT_VARIANCE : IN_VARIANCE;
078
079 PsiType bound = wildcardType.getBound();
080 assert bound != null;
081 return new TypeProjection(variance, transformToType(bound, UPPER_BOUND, typeVariableByPsiResolver));
082 }
083
084 @Override
085 public TypeProjection visitType(PsiType type) {
086 return new TypeProjection(transformToType(type, howThisTypeIsUsed, typeVariableByPsiResolver));
087 }
088 });
089 return result;
090 }
091
092 @NotNull
093 public JetType transformToType(@NotNull String kotlinSignature, TypeVariableResolver typeVariableResolver) {
094 final JetType[] r = new JetType[1];
095 JetTypeJetSignatureReader reader = new JetTypeJetSignatureReader(javaSemanticServices, KotlinBuiltIns.getInstance(), typeVariableResolver) {
096 @Override
097 protected void done(@NotNull JetType jetType) {
098 r[0] = jetType;
099 }
100 };
101 new JetSignatureReader(kotlinSignature).acceptType(reader);
102 return r[0];
103 }
104
105 @NotNull
106 public JetType transformToType(@NotNull PsiType javaType,
107 @NotNull TypeVariableResolver typeVariableResolver) {
108 return transformToType(javaType, TypeUsage.MEMBER_SIGNATURE_INVARIANT, typeVariableResolver);
109 }
110
111 @NotNull
112 public JetType transformToType(@NotNull PsiType javaType, @NotNull final TypeUsage howThisTypeIsUsed,
113 @NotNull final TypeVariableResolver typeVariableResolver) {
114 JetType result = javaType.accept(new PsiTypeVisitor<JetType>() {
115 @Override
116 public JetType visitClassType(PsiClassType classType) {
117 PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
118 PsiClass psiClass = classResolveResult.getElement();
119 if (psiClass == null) {
120 return ErrorUtils.createErrorType("Unresolved java class: " + classType.getPresentableText());
121 }
122
123 if (psiClass instanceof PsiTypeParameter) {
124 PsiTypeParameter typeParameter = (PsiTypeParameter) psiClass;
125
126 PsiTypeParameterListOwner typeParameterListOwner = typeParameter.getOwner();
127 if (typeParameterListOwner instanceof PsiMethod) {
128 PsiMethod psiMethod = (PsiMethod) typeParameterListOwner;
129 if (psiMethod.isConstructor()) {
130 Set<JetType> supertypesJet = Sets.newHashSet();
131 for (PsiClassType supertype : typeParameter.getExtendsListTypes()) {
132 supertypesJet.add(transformToType(supertype, UPPER_BOUND, typeVariableResolver));
133 }
134 return TypeUtils.intersect(JetTypeChecker.INSTANCE, supertypesJet);
135 }
136 }
137
138 TypeParameterDescriptor typeParameterDescriptor = typeVariableResolver.getTypeVariable(typeParameter.getName());
139
140 // In Java: ArrayList<T>
141 // In Kotlin: ArrayList<T>, not ArrayList<T?>
142 // nullability will be taken care of in individual member signatures
143 boolean nullable = !EnumSet.of(TYPE_ARGUMENT, UPPER_BOUND, SUPERTYPE_ARGUMENT).contains(howThisTypeIsUsed);
144 if (nullable) {
145 return TypeUtils.makeNullable(typeParameterDescriptor.getDefaultType());
146 }
147 else {
148 return typeParameterDescriptor.getDefaultType();
149 }
150 }
151 else {
152 // 'L extends List<T>' in Java is a List<T> in Kotlin, not a List<T?>
153 boolean nullable = !EnumSet.of(TYPE_ARGUMENT, SUPERTYPE_ARGUMENT, SUPERTYPE).contains(howThisTypeIsUsed);
154
155 ClassDescriptor classData = JavaToKotlinClassMap.getInstance().mapKotlinClass(new FqName(psiClass.getQualifiedName()),
156 howThisTypeIsUsed);
157
158 if (classData == null) {
159 classData = resolver.resolveClass(new FqName(psiClass.getQualifiedName()), DescriptorSearchRule.INCLUDE_KOTLIN);
160 }
161 if (classData == null) {
162 return ErrorUtils.createErrorType("Unresolved java class: " + classType.getPresentableText());
163 }
164
165 List<TypeProjection> arguments = Lists.newArrayList();
166 List<TypeParameterDescriptor> parameters = classData.getTypeConstructor().getParameters();
167 if (isRaw(classType, !parameters.isEmpty())) {
168 for (TypeParameterDescriptor parameter : parameters) {
169 // not making a star projection because of this case:
170 // Java:
171 // class C<T extends C> {}
172 // The upper bound is raw here, and we can't compute the projection: it would be infinite:
173 // C<*> = C<out C<out C<...>>>
174 // this way we loose some type information, even when the case is not so bad, but it doesn't seem to matter
175
176 // projections are not allowed in immediate arguments of supertypes
177 Variance projectionKind = parameter.getVariance() == OUT_VARIANCE || howThisTypeIsUsed == SUPERTYPE
178 ? INVARIANT
179 : OUT_VARIANCE;
180 arguments.add(new TypeProjection(projectionKind, KotlinBuiltIns.getInstance().getNullableAnyType()));
181 }
182 }
183 else {
184 PsiType[] psiArguments = classType.getParameters();
185
186 if (parameters.size() != psiArguments.length) {
187 // Most of the time this means there is an error in the Java code
188 LOG.warn("parameters = " + parameters.size() + ", actual arguments = " + psiArguments.length +
189 " in " + classType.getPresentableText() + "\n PsiClass: \n" + psiClass.getText());
190
191 for (TypeParameterDescriptor parameter : parameters) {
192 arguments.add(new TypeProjection(ErrorUtils.createErrorType(parameter.getName().asString())));
193 }
194 }
195 else {
196 for (int i = 0; i < parameters.size(); i++) {
197 PsiType psiArgument = psiArguments[i];
198 TypeParameterDescriptor typeParameterDescriptor = parameters.get(i);
199
200 TypeUsage howTheProjectionIsUsed = howThisTypeIsUsed == SUPERTYPE ? SUPERTYPE_ARGUMENT : TYPE_ARGUMENT;
201 TypeProjection typeProjection = transformToTypeProjection(
202 psiArgument, typeParameterDescriptor, typeVariableResolver, howTheProjectionIsUsed);
203
204 if (typeProjection.getProjectionKind() == typeParameterDescriptor.getVariance()) {
205 // remove redundant 'out' and 'in'
206 arguments.add(new TypeProjection(INVARIANT, typeProjection.getType()));
207 }
208 else {
209 arguments.add(typeProjection);
210 }
211 }
212 }
213 }
214
215 return new JetTypeImpl(
216 Collections.<AnnotationDescriptor>emptyList(),
217 classData.getTypeConstructor(),
218 nullable,
219 arguments,
220 classData.getMemberScope(arguments));
221 }
222 }
223
224 @Override
225 public JetType visitPrimitiveType(PsiPrimitiveType primitiveType) {
226 String canonicalText = primitiveType.getCanonicalText();
227 JetType type = JavaToKotlinClassMap.getInstance().mapPrimitiveKotlinClass(canonicalText);
228 assert type != null : canonicalText;
229 return type;
230 }
231
232 @Override
233 public JetType visitArrayType(PsiArrayType arrayType) {
234 PsiType componentType = arrayType.getComponentType();
235 if (componentType instanceof PsiPrimitiveType) {
236 JetType jetType = JavaToKotlinClassMap.getInstance().mapPrimitiveKotlinClass("[" + componentType.getCanonicalText());
237 if (jetType != null)
238 return TypeUtils.makeNullable(jetType);
239 }
240
241 boolean vararg = arrayType instanceof PsiEllipsisType;
242
243 Variance projectionKind = arrayElementTypeProjectionKind(vararg);
244 TypeUsage howArgumentTypeIsUsed = vararg ? MEMBER_SIGNATURE_CONTRAVARIANT : TYPE_ARGUMENT;
245
246 JetType type = transformToType(componentType, howArgumentTypeIsUsed, typeVariableResolver);
247 return TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getArrayType(projectionKind, type));
248 }
249
250 private Variance arrayElementTypeProjectionKind(boolean vararg) {
251 Variance variance;
252 if (howThisTypeIsUsed == MEMBER_SIGNATURE_CONTRAVARIANT && !vararg) {
253 variance = OUT_VARIANCE;
254 }
255 else {
256 variance = INVARIANT;
257 }
258 return variance;
259 }
260
261 @Override
262 public JetType visitType(PsiType type) {
263 throw new UnsupportedOperationException("Unsupported type: " + type.getPresentableText()); // TODO
264 }
265 });
266 return result;
267 }
268
269 private static boolean isRaw(@NotNull PsiClassType classType, boolean argumentsExpected) {
270 // The second option is needed because sometimes we get weird versions of JDK classes in the class path,
271 // such as collections with no generics, so the Java types are not raw, formally, but they don't match with
272 // their Kotlin analogs, so we treat them as raw to avoid exceptions
273 return classType.isRaw() || argumentsExpected && classType.getParameterCount() == 0;
274 }
275
276 public static TypeUsage adjustTypeUsageWithMutabilityAnnotations(PsiModifierListOwner owner, TypeUsage originalTypeUsage) {
277 // Overrides type usage in method signature depending on mutability annotation present
278 EnumSet<TypeUsage> signatureTypeUsages =
279 EnumSet.of(TypeUsage.MEMBER_SIGNATURE_COVARIANT, TypeUsage.MEMBER_SIGNATURE_CONTRAVARIANT, TypeUsage.MEMBER_SIGNATURE_INVARIANT);
280 if (!signatureTypeUsages.contains(originalTypeUsage)) {
281 return originalTypeUsage;
282 }
283 if (JavaAnnotationResolver.findAnnotationWithExternal(owner, JvmAbi.JETBRAINS_MUTABLE_ANNOTATION.getFqName().asString()) != null) {
284 return TypeUsage.MEMBER_SIGNATURE_COVARIANT;
285 }
286 if (JavaAnnotationResolver.findAnnotationWithExternal(owner, JvmAbi.JETBRAINS_READONLY_ANNOTATION.getFqName().asString()) != null) {
287 return TypeUsage.MEMBER_SIGNATURE_CONTRAVARIANT;
288 }
289 return originalTypeUsage;
290 }
291 }