001 /*
002 * Copyright 2010-2015 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.kotlin.platform;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.builtins.CompanionObjectMapping;
022 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023 import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor;
024 import org.jetbrains.kotlin.descriptors.ClassDescriptor;
025 import org.jetbrains.kotlin.name.*;
026 import org.jetbrains.kotlin.resolve.DescriptorUtils;
027 import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
028 import org.jetbrains.kotlin.types.KotlinType;
029 import org.jetbrains.kotlin.types.TypeUtils;
030
031 import java.lang.annotation.Annotation;
032 import java.util.*;
033
034 import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES;
035 import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns;
036 import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getFqNameUnsafe;
037
038 public class JavaToKotlinClassMap implements PlatformToKotlinClassMap {
039 public static final JavaToKotlinClassMap INSTANCE = new JavaToKotlinClassMap();
040
041 private final Map<FqNameUnsafe, ClassId> javaToKotlin = new HashMap<FqNameUnsafe, ClassId>();
042 private final Map<FqNameUnsafe, ClassId> kotlinToJava = new HashMap<FqNameUnsafe, ClassId>();
043
044 private final Map<FqNameUnsafe, FqName> mutableToReadOnly = new HashMap<FqNameUnsafe, FqName>();
045 private final Map<FqNameUnsafe, FqName> readOnlyToMutable = new HashMap<FqNameUnsafe, FqName>();
046
047 private JavaToKotlinClassMap() {
048 addTopLevel(Object.class, FQ_NAMES.any);
049 addTopLevel(String.class, FQ_NAMES.string);
050 addTopLevel(CharSequence.class, FQ_NAMES.charSequence);
051 addTopLevel(Throwable.class, FQ_NAMES.throwable);
052 addTopLevel(Cloneable.class, FQ_NAMES.cloneable);
053 addTopLevel(Number.class, FQ_NAMES.number);
054 addTopLevel(Comparable.class, FQ_NAMES.comparable);
055 addTopLevel(Enum.class, FQ_NAMES._enum);
056 addTopLevel(Annotation.class, FQ_NAMES.annotation);
057
058 addMutableReadOnlyPair(Iterable.class, ClassId.topLevel(FQ_NAMES.iterable), FQ_NAMES.mutableIterable);
059 addMutableReadOnlyPair(Iterator.class, ClassId.topLevel(FQ_NAMES.iterator), FQ_NAMES.mutableIterator);
060 addMutableReadOnlyPair(Collection.class, ClassId.topLevel(FQ_NAMES.collection), FQ_NAMES.mutableCollection);
061 addMutableReadOnlyPair(List.class, ClassId.topLevel(FQ_NAMES.list), FQ_NAMES.mutableList);
062 addMutableReadOnlyPair(Set.class, ClassId.topLevel(FQ_NAMES.set), FQ_NAMES.mutableSet);
063 addMutableReadOnlyPair(ListIterator.class, ClassId.topLevel(FQ_NAMES.listIterator), FQ_NAMES.mutableListIterator);
064
065 ClassId mapClassId = ClassId.topLevel(FQ_NAMES.map);
066 addMutableReadOnlyPair(Map.class, mapClassId, FQ_NAMES.mutableMap);
067 addMutableReadOnlyPair(Map.Entry.class, mapClassId.createNestedClassId(FQ_NAMES.mapEntry.shortName()), FQ_NAMES.mutableMapEntry);
068
069 for (JvmPrimitiveType jvmType : JvmPrimitiveType.values()) {
070 add(
071 ClassId.topLevel(jvmType.getWrapperFqName()),
072 ClassId.topLevel(KotlinBuiltIns.getPrimitiveFqName(jvmType.getPrimitiveType()))
073 );
074 }
075
076 for (ClassId classId : CompanionObjectMapping.INSTANCE.allClassesWithIntrinsicCompanions()) {
077 add(ClassId.topLevel(
078 new FqName("kotlin.jvm.internal." + classId.getShortClassName().asString() + "CompanionObject")),
079 classId.createNestedClassId(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT));
080 }
081
082 // TODO: support also functions with >= 23 parameters
083 for (int i = 0; i < 23; i++) {
084 add(ClassId.topLevel(new FqName("kotlin.jvm.functions.Function" + i)), KotlinBuiltIns.getFunctionClassId(i));
085
086 FunctionClassDescriptor.Kind kFunction = FunctionClassDescriptor.Kind.KFunction;
087 String kFun = kFunction.getPackageFqName() + "." + kFunction.getClassNamePrefix();
088 addKotlinToJava(new FqName(kFun + i), ClassId.topLevel(new FqName(kFun)));
089 }
090
091 addKotlinToJava(FQ_NAMES.nothing.toSafe(), classId(Void.class));
092 }
093
094 /**
095 * E.g.
096 * java.lang.String -> kotlin.String
097 * java.lang.Integer -> kotlin.Int
098 * kotlin.jvm.internal.IntCompanionObject -> kotlin.Int.Companion
099 * java.util.List -> kotlin.List
100 * java.util.Map.Entry -> kotlin.Map.Entry
101 * java.lang.Void -> null
102 * kotlin.jvm.functions.Function3 -> kotlin.Function3
103 */
104 @Nullable
105 public ClassId mapJavaToKotlin(@NotNull FqName fqName) {
106 return javaToKotlin.get(fqName.toUnsafe());
107 }
108
109 @Nullable
110 public ClassDescriptor mapJavaToKotlin(@NotNull FqName fqName, @NotNull KotlinBuiltIns builtIns) {
111 ClassId kotlinClassId = mapJavaToKotlin(fqName);
112 return kotlinClassId != null ? builtIns.getBuiltInClassByFqName(kotlinClassId.asSingleFqName()) : null;
113 }
114
115 /**
116 * E.g.
117 * kotlin.Throwable -> java.lang.Throwable
118 * kotlin.Int -> java.lang.Integer
119 * kotlin.Int.Companion -> kotlin.jvm.internal.IntCompanionObject
120 * kotlin.Nothing -> java.lang.Void
121 * kotlin.IntArray -> null
122 * kotlin.Function3 -> kotlin.jvm.functions.Function3
123 * kotlin.reflect.KFunction3 -> kotlin.reflect.KFunction
124 */
125 @Nullable
126 public ClassId mapKotlinToJava(@NotNull FqNameUnsafe kotlinFqName) {
127 return kotlinToJava.get(kotlinFqName);
128 }
129
130 private void addMutableReadOnlyPair(
131 @NotNull Class<?> javaClass,
132 @NotNull ClassId kotlinReadOnlyClassId,
133 @NotNull FqName kotlinMutableFqName
134 ) {
135 ClassId javaClassId = classId(javaClass);
136
137 add(javaClassId, kotlinReadOnlyClassId);
138 addKotlinToJava(kotlinMutableFqName, javaClassId);
139
140 FqName kotlinReadOnlyFqName = kotlinReadOnlyClassId.asSingleFqName();
141 mutableToReadOnly.put(kotlinMutableFqName.toUnsafe(), kotlinReadOnlyFqName);
142 readOnlyToMutable.put(kotlinReadOnlyFqName.toUnsafe(), kotlinMutableFqName);
143 }
144
145 private void add(@NotNull ClassId javaClassId, @NotNull ClassId kotlinClassId) {
146 addJavaToKotlin(javaClassId, kotlinClassId);
147 addKotlinToJava(kotlinClassId.asSingleFqName(), javaClassId);
148 }
149
150 private void addTopLevel(@NotNull Class<?> javaClass, @NotNull FqNameUnsafe kotlinFqName) {
151 addTopLevel(javaClass, kotlinFqName.toSafe());
152 }
153
154 private void addTopLevel(@NotNull Class<?> javaClass, @NotNull FqName kotlinFqName) {
155 add(classId(javaClass), ClassId.topLevel(kotlinFqName));
156 }
157
158 private void addJavaToKotlin(@NotNull ClassId javaClassId, @NotNull ClassId kotlinClassId) {
159 javaToKotlin.put(javaClassId.asSingleFqName().toUnsafe(), kotlinClassId);
160 }
161
162 private void addKotlinToJava(@NotNull FqName kotlinFqNameUnsafe, @NotNull ClassId javaClassId) {
163 kotlinToJava.put(kotlinFqNameUnsafe.toUnsafe(), javaClassId);
164 }
165
166 @NotNull
167 private static ClassId classId(@NotNull Class<?> clazz) {
168 assert !clazz.isPrimitive() && !clazz.isArray() : "Invalid class: " + clazz;
169 Class<?> outer = clazz.getDeclaringClass();
170 return outer == null
171 ? ClassId.topLevel(new FqName(clazz.getCanonicalName()))
172 : classId(outer).createNestedClassId(Name.identifier(clazz.getSimpleName()));
173 }
174
175 public boolean isJavaPlatformClass(@NotNull FqName fqName) {
176 return mapJavaToKotlin(fqName) != null;
177 }
178
179 @NotNull
180 public Collection<ClassDescriptor> mapPlatformClass(@NotNull FqName fqName, @NotNull KotlinBuiltIns builtIns) {
181 ClassDescriptor kotlinAnalog = mapJavaToKotlin(fqName, builtIns);
182 if (kotlinAnalog == null) return Collections.emptySet();
183
184 FqName kotlinMutableAnalogFqName = readOnlyToMutable.get(getFqNameUnsafe(kotlinAnalog));
185 if (kotlinMutableAnalogFqName == null) return Collections.singleton(kotlinAnalog);
186
187 return Arrays.asList(kotlinAnalog, builtIns.getBuiltInClassByFqName(kotlinMutableAnalogFqName));
188 }
189
190 @Override
191 @NotNull
192 public Collection<ClassDescriptor> mapPlatformClass(@NotNull ClassDescriptor classDescriptor) {
193 FqNameUnsafe className = DescriptorUtils.getFqName(classDescriptor);
194 return className.isSafe()
195 ? mapPlatformClass(className.toSafe(), getBuiltIns(classDescriptor))
196 : Collections.<ClassDescriptor>emptySet();
197 }
198
199 public boolean isMutable(@NotNull ClassDescriptor mutable) {
200 return mutableToReadOnly.containsKey(DescriptorUtils.getFqName(mutable));
201 }
202
203 public boolean isMutable(@NotNull KotlinType type) {
204 ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
205 return classDescriptor != null && isMutable(classDescriptor);
206 }
207
208 public boolean isReadOnly(@NotNull ClassDescriptor readOnly) {
209 return readOnlyToMutable.containsKey(DescriptorUtils.getFqName(readOnly));
210 }
211
212 public boolean isReadOnly(@NotNull KotlinType type) {
213 ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
214 return classDescriptor != null && isReadOnly(classDescriptor);
215 }
216
217 @NotNull
218 public ClassDescriptor convertMutableToReadOnly(@NotNull ClassDescriptor mutable) {
219 return convertToOppositeMutability(mutable, mutableToReadOnly, "mutable");
220 }
221
222 @NotNull
223 private static ClassDescriptor convertToOppositeMutability(
224 @NotNull ClassDescriptor descriptor,
225 @NotNull Map<FqNameUnsafe, FqName> map,
226 @NotNull String mutabilityKindName
227 ) {
228 FqName oppositeClassFqName = map.get(DescriptorUtils.getFqName(descriptor));
229 if (oppositeClassFqName == null) {
230 throw new IllegalArgumentException("Given class " + descriptor + " is not a " + mutabilityKindName + " collection");
231 }
232 return getBuiltIns(descriptor).getBuiltInClassByFqName(oppositeClassFqName);
233 }
234
235 @NotNull
236 public ClassDescriptor convertReadOnlyToMutable(@NotNull ClassDescriptor readOnly) {
237 return convertToOppositeMutability(readOnly, readOnlyToMutable, "read-only");
238 }
239 }