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