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.ClassId;
026 import org.jetbrains.kotlin.name.FqName;
027 import org.jetbrains.kotlin.name.FqNameUnsafe;
028 import org.jetbrains.kotlin.name.Name;
029 import org.jetbrains.kotlin.resolve.DescriptorUtils;
030 import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
031 import org.jetbrains.kotlin.types.KotlinType;
032 import org.jetbrains.kotlin.types.TypeUtils;
033
034 import java.lang.annotation.Annotation;
035 import java.util.*;
036
037 public class JavaToKotlinClassMap implements PlatformToKotlinClassMap {
038 public static final JavaToKotlinClassMap INSTANCE = new JavaToKotlinClassMap();
039
040 private final Map<FqName, ClassDescriptor> javaToKotlin = new HashMap<FqName, ClassDescriptor>();
041 private final Map<FqNameUnsafe, ClassId> kotlinToJava = new HashMap<FqNameUnsafe, ClassId>();
042
043 private final Map<ClassDescriptor, ClassDescriptor> mutableToReadOnly = new HashMap<ClassDescriptor, ClassDescriptor>();
044 private final Map<ClassDescriptor, ClassDescriptor> readOnlyToMutable = new HashMap<ClassDescriptor, ClassDescriptor>();
045 private final CompanionObjectMapping companionObjectMapping;
046
047 private JavaToKotlinClassMap() {
048 KotlinBuiltIns builtIns = JvmBuiltIns.getInstance();
049
050 add(Object.class, builtIns.getAny());
051 add(String.class, builtIns.getString());
052 add(CharSequence.class, builtIns.getCharSequence());
053 add(Throwable.class, builtIns.getThrowable());
054 add(Cloneable.class, builtIns.getCloneable());
055 add(Number.class, builtIns.getNumber());
056 add(Comparable.class, builtIns.getComparable());
057 add(Enum.class, builtIns.getEnum());
058 add(Annotation.class, builtIns.getAnnotation());
059
060 add(Iterable.class, builtIns.getIterable(), builtIns.getMutableIterable());
061 add(Iterator.class, builtIns.getIterator(), builtIns.getMutableIterator());
062 add(Collection.class, builtIns.getCollection(), builtIns.getMutableCollection());
063 add(List.class, builtIns.getList(), builtIns.getMutableList());
064 add(Set.class, builtIns.getSet(), builtIns.getMutableSet());
065 add(Map.class, builtIns.getMap(), builtIns.getMutableMap());
066 add(Map.Entry.class, builtIns.getMapEntry(), builtIns.getMutableMapEntry());
067 add(ListIterator.class, builtIns.getListIterator(), builtIns.getMutableListIterator());
068
069 for (JvmPrimitiveType jvmType : JvmPrimitiveType.values()) {
070 add(ClassId.topLevel(jvmType.getWrapperFqName()), builtIns.getPrimitiveClassDescriptor(jvmType.getPrimitiveType()));
071 }
072
073 companionObjectMapping = new CompanionObjectMapping(builtIns);
074 for (ClassDescriptor descriptor : companionObjectMapping.allClassesWithIntrinsicCompanions()) {
075 ClassDescriptor companion = descriptor.getCompanionObjectDescriptor();
076 assert companion != null : "No companion object found for " + descriptor;
077 add(ClassId.topLevel(new FqName("kotlin.jvm.internal." + descriptor.getName().asString() + "CompanionObject")), companion);
078 }
079
080 // TODO: support also functions with >= 23 parameters
081 for (int i = 0; i < 23; i++) {
082 add(ClassId.topLevel(new FqName("kotlin.jvm.functions.Function" + i)), builtIns.getFunction(i));
083
084 FunctionClassDescriptor.Kind kFunction = FunctionClassDescriptor.Kind.KFunction;
085 String kFun = kFunction.getPackageFqName() + "." + kFunction.getClassNamePrefix();
086 addKotlinToJava(new FqNameUnsafe(kFun + i), ClassId.topLevel(new FqName(kFun)));
087 }
088
089 addKotlinToJava(builtIns.getNothing(), classId(Void.class));
090 }
091
092 /**
093 * E.g.
094 * java.lang.String -> kotlin.String
095 * java.lang.Integer -> kotlin.Int
096 * kotlin.jvm.internal.IntCompanionObject -> kotlin.Int.Companion
097 * java.util.List -> kotlin.List
098 * java.util.Map.Entry -> kotlin.Map.Entry
099 * java.lang.Void -> null
100 * kotlin.jvm.functions.Function3 -> kotlin.Function3
101 */
102 @Nullable
103 public ClassDescriptor mapJavaToKotlin(@NotNull FqName fqName) {
104 return javaToKotlin.get(fqName);
105 }
106
107 /**
108 * E.g.
109 * kotlin.Throwable -> java.lang.Throwable
110 * kotlin.Int -> java.lang.Integer
111 * kotlin.Int.Companion -> kotlin.jvm.internal.IntCompanionObject
112 * kotlin.Nothing -> java.lang.Void
113 * kotlin.IntArray -> null
114 * kotlin.Function3 -> kotlin.jvm.functions.Function3
115 * kotlin.reflect.KFunction3 -> kotlin.reflect.KFunction
116 */
117 @Nullable
118 public ClassId mapKotlinToJava(@NotNull FqNameUnsafe kotlinFqName) {
119 return kotlinToJava.get(kotlinFqName);
120 }
121
122 public boolean isMappedCompanion(@NotNull ClassDescriptor descriptor) {
123 return companionObjectMapping.hasMappingToObject(descriptor);
124 }
125
126 private void add(
127 @NotNull Class<?> javaClass,
128 @NotNull ClassDescriptor kotlinDescriptor,
129 @NotNull ClassDescriptor kotlinMutableDescriptor
130 ) {
131 ClassId javaClassId = classId(javaClass);
132
133 add(javaClassId, kotlinDescriptor);
134 addKotlinToJava(kotlinMutableDescriptor, javaClassId);
135
136 mutableToReadOnly.put(kotlinMutableDescriptor, kotlinDescriptor);
137 readOnlyToMutable.put(kotlinDescriptor, kotlinMutableDescriptor);
138 }
139
140 private void add(@NotNull ClassId javaClassId, @NotNull ClassDescriptor kotlinDescriptor) {
141 addJavaToKotlin(javaClassId, kotlinDescriptor);
142 addKotlinToJava(kotlinDescriptor, javaClassId);
143 }
144
145 private void add(@NotNull Class<?> javaClass, @NotNull ClassDescriptor kotlinDescriptor) {
146 add(classId(javaClass), kotlinDescriptor);
147 }
148
149 private void addJavaToKotlin(@NotNull ClassId javaClassId, @NotNull ClassDescriptor kotlinDescriptor) {
150 javaToKotlin.put(javaClassId.asSingleFqName(), kotlinDescriptor);
151 }
152
153 private void addKotlinToJava(@NotNull ClassDescriptor kotlinDescriptor, @NotNull ClassId javaClassId) {
154 addKotlinToJava(DescriptorUtils.getFqName(kotlinDescriptor), javaClassId);
155 }
156
157 private void addKotlinToJava(@NotNull FqNameUnsafe kotlinFqName, @NotNull ClassId javaClassId) {
158 kotlinToJava.put(kotlinFqName, javaClassId);
159 }
160
161 @NotNull
162 private static ClassId classId(@NotNull Class<?> clazz) {
163 assert !clazz.isPrimitive() && !clazz.isArray() : "Invalid class: " + clazz;
164 Class<?> outer = clazz.getDeclaringClass();
165 return outer == null
166 ? ClassId.topLevel(new FqName(clazz.getCanonicalName()))
167 : classId(outer).createNestedClassId(Name.identifier(clazz.getSimpleName()));
168 }
169
170 @NotNull
171 public Collection<ClassDescriptor> mapPlatformClass(@NotNull FqName fqName) {
172 ClassDescriptor kotlinAnalog = mapJavaToKotlin(fqName);
173 if (kotlinAnalog == null) return Collections.emptySet();
174
175 ClassDescriptor kotlinMutableAnalog = readOnlyToMutable.get(kotlinAnalog);
176 if (kotlinMutableAnalog == null) return Collections.singleton(kotlinAnalog);
177
178 return Arrays.asList(kotlinAnalog, kotlinMutableAnalog);
179 }
180
181 @Override
182 @NotNull
183 public Collection<ClassDescriptor> mapPlatformClass(@NotNull ClassDescriptor classDescriptor) {
184 FqNameUnsafe className = DescriptorUtils.getFqName(classDescriptor);
185 return className.isSafe() ? mapPlatformClass(className.toSafe()) : Collections.<ClassDescriptor>emptySet();
186 }
187
188 public boolean isMutable(@NotNull ClassDescriptor mutable) {
189 return mutableToReadOnly.containsKey(mutable);
190 }
191
192 public boolean isMutable(@NotNull KotlinType type) {
193 ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
194 return classDescriptor != null && isMutable(classDescriptor);
195 }
196
197 public boolean isReadOnly(@NotNull ClassDescriptor readOnly) {
198 return readOnlyToMutable.containsKey(readOnly);
199 }
200
201 public boolean isReadOnly(@NotNull KotlinType type) {
202 ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
203 return classDescriptor != null && isReadOnly(classDescriptor);
204 }
205
206 @NotNull
207 public ClassDescriptor convertMutableToReadOnly(@NotNull ClassDescriptor mutable) {
208 ClassDescriptor readOnly = mutableToReadOnly.get(mutable);
209 if (readOnly == null) {
210 throw new IllegalArgumentException("Given class " + mutable + " is not a mutable collection");
211 }
212 return readOnly;
213 }
214
215 @NotNull
216 public ClassDescriptor convertReadOnlyToMutable(@NotNull ClassDescriptor readOnly) {
217 ClassDescriptor mutable = readOnlyToMutable.get(readOnly);
218 if (mutable == null) {
219 throw new IllegalArgumentException("Given class " + readOnly + " is not a read-only collection");
220 }
221 return mutable;
222 }
223 }