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