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    }