001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve.java.mapping;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.asm4.Type;
022    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
024    import org.jetbrains.jet.lang.resolve.java.AsmTypeConstants;
025    import org.jetbrains.jet.lang.resolve.java.JvmClassName;
026    import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
027    import org.jetbrains.jet.lang.resolve.name.FqName;
028    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
029    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
030    import org.jetbrains.jet.lang.types.lang.PrimitiveType;
031    
032    import java.util.HashMap;
033    import java.util.Map;
034    
035    import static org.jetbrains.jet.lang.resolve.java.mapping.PrimitiveTypesUtil.asmTypeForPrimitive;
036    
037    public class KotlinToJavaTypesMap extends JavaToKotlinClassMapBuilder {
038        private static KotlinToJavaTypesMap instance = null;
039    
040        @NotNull
041        public static KotlinToJavaTypesMap getInstance() {
042            if (instance == null) {
043                instance = new KotlinToJavaTypesMap();
044            }
045            return instance;
046        }
047    
048        private final Map<FqName, Type> asmTypes = new HashMap<FqName, Type>();
049        private final Map<FqName, Type> asmNullableTypes = new HashMap<FqName, Type>();
050        private final Map<FqName, FqName> kotlinToJavaFqName = new HashMap<FqName, FqName>();
051    
052        private KotlinToJavaTypesMap() {
053            init();
054            initPrimitives();
055        }
056    
057        private void initPrimitives() {
058            FqName builtInsFqName = KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME;
059            for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
060                PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
061                Type asmType = asmTypeForPrimitive(jvmPrimitiveType);
062                FqName fqName = builtInsFqName.child(primitiveType.getTypeName());
063    
064                register(fqName, asmType);
065    
066                FqName wrapperFqName = jvmPrimitiveType.getWrapperFqName();
067                registerNullable(fqName, Type.getObjectType(JvmClassName.byFqNameWithoutInnerClasses(wrapperFqName).getInternalName()));
068                registerFqName(fqName, wrapperFqName);
069    
070                register(builtInsFqName.child(primitiveType.getArrayTypeName()), Type.getType("[" + asmType.getDescriptor()));
071            }
072        }
073    
074        @Nullable
075        public Type getJavaAnalog(@NotNull FqName fqName, boolean isNullable) {
076            if (isNullable) {
077                Type nullableType = asmNullableTypes.get(fqName);
078                if (nullableType != null) {
079                    return nullableType;
080                }
081            }
082            return asmTypes.get(fqName);
083        }
084    
085        /**
086         * E.g.
087         * jet.Throwable -> java.lang.Throwable
088         * jet.Deprecated -> java.lang.annotation.Deprecated
089         * jet.Int -> java.lang.Integer
090         * jet.IntArray -> null
091         */
092        @Nullable
093        public FqName getKotlinToJavaFqName(@NotNull FqName fqName) {
094            return kotlinToJavaFqName.get(fqName);
095        }
096    
097        @Override
098        protected void register(@NotNull Class<?> javaClass, @NotNull ClassDescriptor kotlinDescriptor, @NotNull Direction direction) {
099            if (direction == Direction.BOTH || direction == Direction.KOTLIN_TO_JAVA) {
100                FqNameUnsafe fqNameUnsafe = DescriptorUtils.getFQName(kotlinDescriptor);
101                assert fqNameUnsafe.isSafe() : "FQ name of a mapped class should be safe: " + fqNameUnsafe;
102                FqName fqName = fqNameUnsafe.toSafe();
103                register(fqName, AsmTypeConstants.getType(javaClass));
104                registerFqName(fqName, new FqName(javaClass.getCanonicalName()));
105            }
106        }
107    
108        @Override
109        protected void register(
110                @NotNull Class<?> javaClass,
111                @NotNull ClassDescriptor kotlinDescriptor,
112                @NotNull ClassDescriptor kotlinMutableDescriptor,
113                @NotNull Direction direction
114        ) {
115            if (direction == Direction.BOTH || direction == Direction.KOTLIN_TO_JAVA) {
116                register(javaClass, kotlinDescriptor);
117                register(javaClass, kotlinMutableDescriptor);
118            }
119        }
120    
121        private void register(@NotNull FqName fqName, @NotNull Type type) {
122            asmTypes.put(fqName, type);
123        }
124    
125        private void registerNullable(@NotNull FqName fqName, @NotNull Type nullableType) {
126            asmNullableTypes.put(fqName, nullableType);
127        }
128    
129        private void registerFqName(@NotNull FqName kotlinFqName, @NotNull FqName javaFqName) {
130            kotlinToJavaFqName.put(kotlinFqName, javaFqName);
131        }
132    }