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.ClassDescriptor;
022 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
023 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
024 import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
025 import org.jetbrains.jet.lang.resolve.java.structure.*;
026 import org.jetbrains.jet.lang.resolve.name.FqName;
027 import org.jetbrains.jet.lang.types.*;
028 import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
029 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
030
031 import javax.inject.Inject;
032 import java.util.*;
033
034 import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.INCLUDE_KOTLIN_SOURCES;
035 import static org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage.*;
036 import static org.jetbrains.jet.lang.types.Variance.*;
037
038 public class JavaTypeTransformer {
039 private JavaClassResolver classResolver;
040
041 @Inject
042 public void setClassResolver(JavaClassResolver classResolver) {
043 this.classResolver = classResolver;
044 }
045
046 @NotNull
047 private TypeProjection transformToTypeProjection(
048 @NotNull JavaType type,
049 @NotNull TypeParameterDescriptor typeParameterDescriptor,
050 @NotNull TypeVariableResolver typeVariableResolver,
051 @NotNull TypeUsage howThisTypeIsUsed
052 ) {
053 if (!(type instanceof JavaWildcardType)) {
054 return new TypeProjectionImpl(transformToType(type, howThisTypeIsUsed, typeVariableResolver));
055 }
056
057 JavaWildcardType wildcardType = (JavaWildcardType) type;
058 JavaType bound = wildcardType.getBound();
059 if (bound == null) {
060 return SubstitutionUtils.makeStarProjection(typeParameterDescriptor);
061 }
062
063 Variance variance = wildcardType.isExtends() ? OUT_VARIANCE : IN_VARIANCE;
064
065 return new TypeProjectionImpl(variance, transformToType(bound, UPPER_BOUND, typeVariableResolver));
066 }
067
068 @NotNull
069 public JetType transformToType(@NotNull JavaType type, @NotNull TypeVariableResolver typeVariableResolver) {
070 return transformToType(type, TypeUsage.MEMBER_SIGNATURE_INVARIANT, typeVariableResolver);
071 }
072
073 @NotNull
074 public JetType transformToType(
075 @NotNull JavaType type,
076 @NotNull TypeUsage howThisTypeIsUsed,
077 @NotNull TypeVariableResolver typeVariableResolver
078 ) {
079 if (type instanceof JavaClassifierType) {
080 JavaClassifierType classifierType = (JavaClassifierType) type;
081 JetType jetType = transformClassifierType(classifierType, howThisTypeIsUsed, typeVariableResolver);
082 if (jetType == null) {
083 return ErrorUtils.createErrorType("Unresolved java classifier: " + classifierType.getPresentableText());
084 }
085 return jetType;
086 }
087 else if (type instanceof JavaPrimitiveType) {
088 String canonicalText = ((JavaPrimitiveType) type).getCanonicalText();
089 JetType jetType = JavaToKotlinClassMap.getInstance().mapPrimitiveKotlinClass(canonicalText);
090 assert jetType != null : "Primitive type is not found: " + canonicalText;
091 return jetType;
092 }
093 else if (type instanceof JavaArrayType) {
094 return transformArrayType((JavaArrayType) type, howThisTypeIsUsed, typeVariableResolver, false);
095 }
096 else {
097 throw new UnsupportedOperationException("Unsupported type: " + type); // TODO
098 }
099 }
100
101 @Nullable
102 private JetType transformClassifierType(
103 @NotNull JavaClassifierType classifierType,
104 @NotNull TypeUsage howThisTypeIsUsed,
105 @NotNull TypeVariableResolver typeVariableResolver
106 ) {
107 JavaClassifier javaClassifier = classifierType.getClassifier();
108 if (javaClassifier == null) {
109 return null;
110 }
111 if (javaClassifier instanceof JavaTypeParameter) {
112 return transformTypeParameter((JavaTypeParameter) javaClassifier, howThisTypeIsUsed, typeVariableResolver);
113 }
114 else if (javaClassifier instanceof JavaClass) {
115 FqName fqName = ((JavaClass) javaClassifier).getFqName();
116 assert fqName != null : "Class type should have a FQ name: " + javaClassifier;
117 return transformClassType(fqName, classifierType, howThisTypeIsUsed, typeVariableResolver);
118 }
119 else {
120 throw new UnsupportedOperationException("Unsupported classifier: " + javaClassifier);
121 }
122 }
123
124 @Nullable
125 private JetType transformTypeParameter(
126 @NotNull JavaTypeParameter typeParameter,
127 @NotNull TypeUsage howThisTypeIsUsed,
128 @NotNull TypeVariableResolver typeVariableResolver
129 ) {
130 JavaTypeParameterListOwner owner = typeParameter.getOwner();
131 if (owner instanceof JavaMethod && ((JavaMethod) owner).isConstructor()) {
132 Set<JetType> supertypesJet = new HashSet<JetType>();
133 for (JavaClassifierType supertype : typeParameter.getUpperBounds()) {
134 supertypesJet.add(transformToType(supertype, UPPER_BOUND, typeVariableResolver));
135 }
136 return TypeUtils.intersect(JetTypeChecker.INSTANCE, supertypesJet);
137 }
138
139 TypeParameterDescriptor descriptor = typeVariableResolver.getTypeVariable(typeParameter.getName());
140 if (descriptor == null) return null;
141
142 // In Java: ArrayList<T>
143 // In Kotlin: ArrayList<T>, not ArrayList<T?>
144 // nullability will be taken care of in individual member signatures
145 boolean nullable = !EnumSet.of(TYPE_ARGUMENT, UPPER_BOUND, SUPERTYPE_ARGUMENT).contains(howThisTypeIsUsed);
146
147 return TypeUtils.makeNullableIfNeeded(descriptor.getDefaultType(), nullable);
148 }
149
150 @Nullable
151 private JetType transformClassType(
152 @NotNull FqName fqName,
153 @NotNull JavaClassifierType classifierType,
154 @NotNull TypeUsage howThisTypeIsUsed,
155 @NotNull TypeVariableResolver typeVariableResolver
156 ) {
157 // 'L extends List<T>' in Java is a List<T> in Kotlin, not a List<T?>
158 boolean nullable = !EnumSet.of(TYPE_ARGUMENT, SUPERTYPE_ARGUMENT, SUPERTYPE).contains(howThisTypeIsUsed);
159
160 ClassDescriptor classData = JavaToKotlinClassMap.getInstance().mapKotlinClass(fqName, howThisTypeIsUsed);
161
162 if (classData == null) {
163 classData = classResolver.resolveClass(fqName, INCLUDE_KOTLIN_SOURCES);
164 }
165 if (classData == null) {
166 return null;
167 }
168
169 List<TypeProjection> arguments = new ArrayList<TypeProjection>();
170 List<TypeParameterDescriptor> parameters = classData.getTypeConstructor().getParameters();
171 if (isRaw(classifierType, !parameters.isEmpty())) {
172 for (TypeParameterDescriptor parameter : parameters) {
173 // not making a star projection because of this case:
174 // Java:
175 // class C<T extends C> {}
176 // The upper bound is raw here, and we can't compute the projection: it would be infinite:
177 // C<*> = C<out C<out C<...>>>
178 // this way we loose some type information, even when the case is not so bad, but it doesn't seem to matter
179
180 // projections are not allowed in immediate arguments of supertypes
181 Variance projectionKind = parameter.getVariance() == OUT_VARIANCE || howThisTypeIsUsed == SUPERTYPE
182 ? INVARIANT
183 : OUT_VARIANCE;
184 arguments.add(new TypeProjectionImpl(projectionKind, KotlinBuiltIns.getInstance().getNullableAnyType()));
185 }
186 }
187 else {
188 List<JavaType> javaTypeArguments = classifierType.getTypeArguments();
189
190 if (parameters.size() != javaTypeArguments.size()) {
191 // Most of the time this means there is an error in the Java code
192 for (TypeParameterDescriptor parameter : parameters) {
193 arguments.add(new TypeProjectionImpl(ErrorUtils.createErrorType(parameter.getName().asString())));
194 }
195 }
196 else {
197 for (int i = 0, size = javaTypeArguments.size(); i < size; i++) {
198 JavaType typeArgument = javaTypeArguments.get(i);
199 TypeParameterDescriptor typeParameterDescriptor = parameters.get(i);
200
201 TypeUsage howTheProjectionIsUsed = howThisTypeIsUsed == SUPERTYPE ? SUPERTYPE_ARGUMENT : TYPE_ARGUMENT;
202 TypeProjection typeProjection =
203 transformToTypeProjection(typeArgument, typeParameterDescriptor, typeVariableResolver, howTheProjectionIsUsed);
204
205 if (typeProjection.getProjectionKind() == typeParameterDescriptor.getVariance()) {
206 // remove redundant 'out' and 'in'
207 arguments.add(new TypeProjectionImpl(INVARIANT, typeProjection.getType()));
208 }
209 else {
210 arguments.add(typeProjection);
211 }
212 }
213 }
214 }
215
216 return new JetTypeImpl(
217 Collections.<AnnotationDescriptor>emptyList(),
218 classData.getTypeConstructor(),
219 nullable,
220 arguments,
221 classData.getMemberScope(arguments));
222 }
223
224 @NotNull
225 private JetType transformArrayType(
226 @NotNull JavaArrayType arrayType,
227 @NotNull TypeUsage howThisTypeIsUsed,
228 @NotNull TypeVariableResolver typeVariableResolver,
229 boolean vararg
230 ) {
231 JavaType componentType = arrayType.getComponentType();
232 if (componentType instanceof JavaPrimitiveType) {
233 JetType jetType = JavaToKotlinClassMap.getInstance().mapPrimitiveKotlinClass(
234 "[" + ((JavaPrimitiveType) componentType).getCanonicalText());
235 if (jetType != null) {
236 return TypeUtils.makeNullable(jetType);
237 }
238 }
239
240 Variance projectionKind = arrayElementTypeProjectionKind(howThisTypeIsUsed, vararg);
241 TypeUsage howArgumentTypeIsUsed = vararg ? MEMBER_SIGNATURE_CONTRAVARIANT : TYPE_ARGUMENT;
242
243 JetType type = transformToType(componentType, howArgumentTypeIsUsed, typeVariableResolver);
244 return TypeUtils.makeNullable(KotlinBuiltIns.getInstance().getArrayType(projectionKind, type));
245 }
246
247 @NotNull
248 private static Variance arrayElementTypeProjectionKind(@NotNull TypeUsage howThisTypeIsUsed, boolean vararg) {
249 if (howThisTypeIsUsed == MEMBER_SIGNATURE_CONTRAVARIANT && !vararg) {
250 return OUT_VARIANCE;
251 }
252 else {
253 return INVARIANT;
254 }
255 }
256
257 @NotNull
258 public JetType transformVarargType(
259 @NotNull JavaArrayType type,
260 @NotNull TypeUsage howThisTypeIsUsed,
261 @NotNull TypeVariableResolver typeVariableResolver
262 ) {
263 return transformArrayType(type, howThisTypeIsUsed, typeVariableResolver, true);
264 }
265
266 private static boolean isRaw(@NotNull JavaClassifierType classifierType, boolean argumentsExpected) {
267 // The second option is needed because sometimes we get weird versions of JDK classes in the class path,
268 // such as collections with no generics, so the Java types are not raw, formally, but they don't match with
269 // their Kotlin analogs, so we treat them as raw to avoid exceptions
270 return classifierType.isRaw() || argumentsExpected && classifierType.getTypeArguments().isEmpty();
271 }
272 }