/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.platform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.CompanionObjectMapping;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.name.*;
import org.jetbrains.kotlin.resolve.DescriptorUtils;
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.TypeUtils;
import java.lang.annotation.Annotation;
import java.util.*;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getBuiltIns;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getFqNameUnsafe;
public class JavaToKotlinClassMap implements PlatformToKotlinClassMap {
public static final JavaToKotlinClassMap INSTANCE = new JavaToKotlinClassMap();
private final Map<FqNameUnsafe, ClassId> javaToKotlin = new HashMap<FqNameUnsafe, ClassId>();
private final Map<FqNameUnsafe, ClassId> kotlinToJava = new HashMap<FqNameUnsafe, ClassId>();
private final Map<FqNameUnsafe, FqName> mutableToReadOnly = new HashMap<FqNameUnsafe, FqName>();
private final Map<FqNameUnsafe, FqName> readOnlyToMutable = new HashMap<FqNameUnsafe, FqName>();
private JavaToKotlinClassMap() {
addTopLevel(Object.class, FQ_NAMES.any);
addTopLevel(String.class, FQ_NAMES.string);
addTopLevel(CharSequence.class, FQ_NAMES.charSequence);
addTopLevel(Throwable.class, FQ_NAMES.throwable);
addTopLevel(Cloneable.class, FQ_NAMES.cloneable);
addTopLevel(Number.class, FQ_NAMES.number);
addTopLevel(Comparable.class, FQ_NAMES.comparable);
addTopLevel(Enum.class, FQ_NAMES._enum);
addTopLevel(Annotation.class, FQ_NAMES.annotation);
addMutableReadOnlyPair(Iterable.class, ClassId.topLevel(FQ_NAMES.iterable), FQ_NAMES.mutableIterable);
addMutableReadOnlyPair(Iterator.class, ClassId.topLevel(FQ_NAMES.iterator), FQ_NAMES.mutableIterator);
addMutableReadOnlyPair(Collection.class, ClassId.topLevel(FQ_NAMES.collection), FQ_NAMES.mutableCollection);
addMutableReadOnlyPair(List.class, ClassId.topLevel(FQ_NAMES.list), FQ_NAMES.mutableList);
addMutableReadOnlyPair(Set.class, ClassId.topLevel(FQ_NAMES.set), FQ_NAMES.mutableSet);
addMutableReadOnlyPair(ListIterator.class, ClassId.topLevel(FQ_NAMES.listIterator), FQ_NAMES.mutableListIterator);
ClassId mapClassId = ClassId.topLevel(FQ_NAMES.map);
addMutableReadOnlyPair(Map.class, mapClassId, FQ_NAMES.mutableMap);
addMutableReadOnlyPair(Map.Entry.class, mapClassId.createNestedClassId(FQ_NAMES.mapEntry.shortName()), FQ_NAMES.mutableMapEntry);
for (JvmPrimitiveType jvmType : JvmPrimitiveType.values()) {
add(
ClassId.topLevel(jvmType.getWrapperFqName()),
ClassId.topLevel(KotlinBuiltIns.getPrimitiveFqName(jvmType.getPrimitiveType()))
);
}
for (ClassId classId : CompanionObjectMapping.INSTANCE.allClassesWithIntrinsicCompanions()) {
add(ClassId.topLevel(
new FqName("kotlin.jvm.internal." + classId.getShortClassName().asString() + "CompanionObject")),
classId.createNestedClassId(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT));
}
// TODO: support also functions with >= 23 parameters
for (int i = 0; i < 23; i++) {
add(ClassId.topLevel(new FqName("kotlin.jvm.functions.Function" + i)), KotlinBuiltIns.getFunctionClassId(i));
FunctionClassDescriptor.Kind kFunction = FunctionClassDescriptor.Kind.KFunction;
String kFun = kFunction.getPackageFqName() + "." + kFunction.getClassNamePrefix();
addKotlinToJava(new FqName(kFun + i), ClassId.topLevel(new FqName(kFun)));
}
addKotlinToJava(FQ_NAMES.nothing.toSafe(), classId(Void.class));
}
/**
* E.g.
* java.lang.String -> kotlin.String
* java.lang.Integer -> kotlin.Int
* kotlin.jvm.internal.IntCompanionObject -> kotlin.Int.Companion
* java.util.List -> kotlin.List
* java.util.Map.Entry -> kotlin.Map.Entry
* java.lang.Void -> null
* kotlin.jvm.functions.Function3 -> kotlin.Function3
*/
@Nullable
public ClassId mapJavaToKotlin(@NotNull FqName fqName) {
return javaToKotlin.get(fqName.toUnsafe());
}
@Nullable
public ClassDescriptor mapJavaToKotlin(@NotNull FqName fqName, @NotNull KotlinBuiltIns builtIns) {
ClassId kotlinClassId = mapJavaToKotlin(fqName);
return kotlinClassId != null ? builtIns.getBuiltInClassByFqName(kotlinClassId.asSingleFqName()) : null;
}
/**
* E.g.
* kotlin.Throwable -> java.lang.Throwable
* kotlin.Int -> java.lang.Integer
* kotlin.Int.Companion -> kotlin.jvm.internal.IntCompanionObject
* kotlin.Nothing -> java.lang.Void
* kotlin.IntArray -> null
* kotlin.Function3 -> kotlin.jvm.functions.Function3
* kotlin.reflect.KFunction3 -> kotlin.reflect.KFunction
*/
@Nullable
public ClassId mapKotlinToJava(@NotNull FqNameUnsafe kotlinFqName) {
return kotlinToJava.get(kotlinFqName);
}
private void addMutableReadOnlyPair(
@NotNull Class<?> javaClass,
@NotNull ClassId kotlinReadOnlyClassId,
@NotNull FqName kotlinMutableFqName
) {
ClassId javaClassId = classId(javaClass);
add(javaClassId, kotlinReadOnlyClassId);
addKotlinToJava(kotlinMutableFqName, javaClassId);
FqName kotlinReadOnlyFqName = kotlinReadOnlyClassId.asSingleFqName();
mutableToReadOnly.put(kotlinMutableFqName.toUnsafe(), kotlinReadOnlyFqName);
readOnlyToMutable.put(kotlinReadOnlyFqName.toUnsafe(), kotlinMutableFqName);
}
private void add(@NotNull ClassId javaClassId, @NotNull ClassId kotlinClassId) {
addJavaToKotlin(javaClassId, kotlinClassId);
addKotlinToJava(kotlinClassId.asSingleFqName(), javaClassId);
}
private void addTopLevel(@NotNull Class<?> javaClass, @NotNull FqNameUnsafe kotlinFqName) {
addTopLevel(javaClass, kotlinFqName.toSafe());
}
private void addTopLevel(@NotNull Class<?> javaClass, @NotNull FqName kotlinFqName) {
add(classId(javaClass), ClassId.topLevel(kotlinFqName));
}
private void addJavaToKotlin(@NotNull ClassId javaClassId, @NotNull ClassId kotlinClassId) {
javaToKotlin.put(javaClassId.asSingleFqName().toUnsafe(), kotlinClassId);
}
private void addKotlinToJava(@NotNull FqName kotlinFqNameUnsafe, @NotNull ClassId javaClassId) {
kotlinToJava.put(kotlinFqNameUnsafe.toUnsafe(), javaClassId);
}
@NotNull
private static ClassId classId(@NotNull Class<?> clazz) {
assert !clazz.isPrimitive() && !clazz.isArray() : "Invalid class: " + clazz;
Class<?> outer = clazz.getDeclaringClass();
return outer == null
? ClassId.topLevel(new FqName(clazz.getCanonicalName()))
: classId(outer).createNestedClassId(Name.identifier(clazz.getSimpleName()));
}
public boolean isJavaPlatformClass(@NotNull FqName fqName) {
return mapJavaToKotlin(fqName) != null;
}
@NotNull
public Collection<ClassDescriptor> mapPlatformClass(@NotNull FqName fqName, @NotNull KotlinBuiltIns builtIns) {
ClassDescriptor kotlinAnalog = mapJavaToKotlin(fqName, builtIns);
if (kotlinAnalog == null) return Collections.emptySet();
FqName kotlinMutableAnalogFqName = readOnlyToMutable.get(getFqNameUnsafe(kotlinAnalog));
if (kotlinMutableAnalogFqName == null) return Collections.singleton(kotlinAnalog);
return Arrays.asList(kotlinAnalog, builtIns.getBuiltInClassByFqName(kotlinMutableAnalogFqName));
}
@Override
@NotNull
public Collection<ClassDescriptor> mapPlatformClass(@NotNull ClassDescriptor classDescriptor) {
FqNameUnsafe className = DescriptorUtils.getFqName(classDescriptor);
return className.isSafe()
? mapPlatformClass(className.toSafe(), getBuiltIns(classDescriptor))
: Collections.<ClassDescriptor>emptySet();
}
public boolean isMutable(@NotNull ClassDescriptor mutable) {
return mutableToReadOnly.containsKey(DescriptorUtils.getFqName(mutable));
}
public boolean isMutable(@NotNull KotlinType type) {
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
return classDescriptor != null && isMutable(classDescriptor);
}
public boolean isReadOnly(@NotNull ClassDescriptor readOnly) {
return readOnlyToMutable.containsKey(DescriptorUtils.getFqName(readOnly));
}
public boolean isReadOnly(@NotNull KotlinType type) {
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(type);
return classDescriptor != null && isReadOnly(classDescriptor);
}
@NotNull
public ClassDescriptor convertMutableToReadOnly(@NotNull ClassDescriptor mutable) {
return convertToOppositeMutability(mutable, mutableToReadOnly, "mutable");
}
@NotNull
private static ClassDescriptor convertToOppositeMutability(
@NotNull ClassDescriptor descriptor,
@NotNull Map<FqNameUnsafe, FqName> map,
@NotNull String mutabilityKindName
) {
FqName oppositeClassFqName = map.get(DescriptorUtils.getFqName(descriptor));
if (oppositeClassFqName == null) {
throw new IllegalArgumentException("Given class " + descriptor + " is not a " + mutabilityKindName + " collection");
}
return getBuiltIns(descriptor).getBuiltInClassByFqName(oppositeClassFqName);
}
@NotNull
public ClassDescriptor convertReadOnlyToMutable(@NotNull ClassDescriptor readOnly) {
return convertToOppositeMutability(readOnly, readOnlyToMutable, "read-only");
}
}