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