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    }