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