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
017package org.jetbrains.jet.lang.resolve.java;
018
019import com.google.common.collect.Maps;
020import com.google.common.collect.Sets;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.asm4.Type;
024import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
026import org.jetbrains.jet.lang.resolve.DescriptorUtils;
027import org.jetbrains.jet.lang.resolve.name.FqName;
028import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
029import org.jetbrains.jet.lang.types.JetType;
030import org.jetbrains.jet.lang.types.lang.PrimitiveType;
031
032import java.util.Map;
033import java.util.Set;
034
035public class KotlinToJavaTypesMap extends JavaToKotlinClassMapBuilder {
036    private static KotlinToJavaTypesMap instance = null;
037
038    @NotNull
039    public static KotlinToJavaTypesMap getInstance() {
040        if (instance == null) {
041            instance = new KotlinToJavaTypesMap();
042        }
043        return instance;
044    }
045
046    private final Map<FqName, Type> asmTypes = Maps.newHashMap();
047    private final Map<FqName, Type> asmNullableTypes = Maps.newHashMap();
048    private final Set<String> mappedTypeNames = Sets.newHashSet();
049
050    private KotlinToJavaTypesMap() {
051        init();
052        initPrimitives();
053    }
054
055    private void initPrimitives() {
056        for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
057            FqName className = jvmPrimitiveType.getPrimitiveType().getClassName();
058
059            register(className, jvmPrimitiveType.getAsmType());
060            registerNullable(className, jvmPrimitiveType.getWrapper().getAsmType());
061        }
062        for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
063            PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
064            register(primitiveType.getArrayClassName(), jvmPrimitiveType.getAsmArrayType());
065        }
066    }
067
068    @Nullable
069    public Type getJavaAnalog(@NotNull JetType jetType) {
070        ClassifierDescriptor classifier = jetType.getConstructor().getDeclarationDescriptor();
071        assert classifier != null;
072        FqNameUnsafe className = DescriptorUtils.getFQName(classifier);
073        if (!className.isSafe()) return null;
074        FqName fqName = className.toSafe();
075        if (jetType.isNullable()) {
076            Type nullableType = asmNullableTypes.get(fqName);
077            if (nullableType != null) {
078                return nullableType;
079            }
080        }
081        return asmTypes.get(fqName);
082    }
083
084    @Override
085    protected void register(
086            @NotNull Class<?> javaClass,
087            @NotNull ClassDescriptor kotlinDescriptor,
088            @NotNull Direction direction
089    ) {
090        if (direction == Direction.BOTH || direction == Direction.KOTLIN_TO_JAVA) {
091            register(kotlinDescriptor, AsmTypeConstants.getType(javaClass));
092        }
093    }
094
095    @Override
096    protected void register(
097            @NotNull Class<?> javaClass,
098            @NotNull ClassDescriptor kotlinDescriptor,
099            @NotNull ClassDescriptor kotlinMutableDescriptor,
100            @NotNull Direction direction
101    ) {
102        if (direction == Direction.BOTH || direction == Direction.KOTLIN_TO_JAVA) {
103            register(javaClass, kotlinDescriptor);
104            register(javaClass, kotlinMutableDescriptor);
105        }
106    }
107
108    private void register(@NotNull ClassDescriptor kotlinDescriptor, @NotNull Type javaType) {
109        FqNameUnsafe fqName = DescriptorUtils.getFQName(kotlinDescriptor);
110        assert fqName.isSafe();
111        register(fqName.toSafe(), javaType);
112    }
113
114    private void register(@NotNull FqName fqName, @NotNull Type type) {
115        mappedTypeNames.add(type.getClassName());
116        asmTypes.put(fqName, type);
117    }
118
119    private void registerNullable(@NotNull FqName fqName, @NotNull Type nullableType) {
120        asmNullableTypes.put(fqName, nullableType);
121    }
122
123    public boolean isForceReal(@NotNull JvmClassName className) {
124        return JvmPrimitiveType.getByWrapperClass(className) != null
125               || mappedTypeNames.contains(className.getFqName().asString());
126    }
127}