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    }