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.*;
020import com.intellij.psi.CommonClassNames;
021import org.jetbrains.annotations.NotNull;
022import org.jetbrains.annotations.Nullable;
023import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
024import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028import org.jetbrains.jet.lang.resolve.name.FqName;
029import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
030import org.jetbrains.jet.lang.types.JetType;
031import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
032import org.jetbrains.jet.lang.types.lang.PrimitiveType;
033
034import java.util.*;
035
036public class JavaToKotlinClassMap extends JavaToKotlinClassMapBuilder implements PlatformToKotlinClassMap {
037    private static JavaToKotlinClassMap instance = null;
038
039    @NotNull
040    public static JavaToKotlinClassMap getInstance() {
041        if (instance == null) {
042            instance = new JavaToKotlinClassMap();
043        }
044        return instance;
045    }
046
047    private final Map<FqName, ClassDescriptor> classDescriptorMap = Maps.newHashMap();
048    private final Map<FqName, ClassDescriptor> classDescriptorMapForCovariantPositions = Maps.newHashMap();
049    private final Map<String, JetType> primitiveTypesMap = Maps.newHashMap();
050    private final Multimap<FqName, ClassDescriptor> packagesWithMappedClasses = HashMultimap.create();
051
052    private JavaToKotlinClassMap() {
053        init();
054        initPrimitives();
055    }
056
057    private void initPrimitives() {
058        KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
059        for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
060            PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
061            register(jvmPrimitiveType.getWrapper().getFqName(), builtIns.getPrimitiveClassDescriptor(primitiveType));
062        }
063
064        for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
065            PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
066            primitiveTypesMap.put(jvmPrimitiveType.getName(), KotlinBuiltIns.getInstance().getPrimitiveJetType(primitiveType));
067            primitiveTypesMap.put("[" + jvmPrimitiveType.getName(), KotlinBuiltIns.getInstance().getPrimitiveArrayJetType(primitiveType));
068            primitiveTypesMap.put(jvmPrimitiveType.getWrapper().getFqName().asString(), KotlinBuiltIns.getInstance().getNullablePrimitiveJetType(
069                    primitiveType));
070        }
071        primitiveTypesMap.put("void", KotlinBuiltIns.getInstance().getUnitType());
072    }
073
074    @Nullable
075    public JetType mapPrimitiveKotlinClass(@NotNull String name) {
076        return primitiveTypesMap.get(name);
077    }
078
079    @Nullable
080    public ClassDescriptor mapKotlinClass(@NotNull FqName fqName, @NotNull TypeUsage typeUsage) {
081        if (typeUsage == TypeUsage.MEMBER_SIGNATURE_COVARIANT
082                || typeUsage == TypeUsage.SUPERTYPE) {
083            ClassDescriptor descriptor = classDescriptorMapForCovariantPositions.get(fqName);
084            if (descriptor != null) {
085                return descriptor;
086            }
087        }
088        return classDescriptorMap.get(fqName);
089    }
090
091    @Nullable
092    public AnnotationDescriptor mapToAnnotationClass(@NotNull FqName fqName) {
093        ClassDescriptor classDescriptor = classDescriptorMap.get(fqName);
094        if (classDescriptor != null && fqName.asString().equals(CommonClassNames.JAVA_LANG_DEPRECATED)) {
095            return DescriptorResolverUtils.getAnnotationDescriptorForJavaLangDeprecated(classDescriptor);
096        }
097        return null;
098    }
099
100    private static FqName getJavaClassFqName(@NotNull Class<?> javaClass) {
101        return new FqName(javaClass.getName().replace('$', '.'));
102    }
103
104    @Override
105    protected void register(
106            @NotNull Class<?> javaClass,
107            @NotNull ClassDescriptor kotlinDescriptor,
108            @NotNull Direction direction
109    ) {
110        if (direction == Direction.BOTH || direction == Direction.JAVA_TO_KOTLIN) {
111            register(getJavaClassFqName(javaClass), kotlinDescriptor);
112        }
113    }
114
115    @Override
116    protected void register(
117            @NotNull Class<?> javaClass,
118            @NotNull ClassDescriptor kotlinDescriptor,
119            @NotNull ClassDescriptor kotlinMutableDescriptor,
120            @NotNull Direction direction
121    ) {
122        if (direction == Direction.BOTH || direction == Direction.JAVA_TO_KOTLIN) {
123            FqName javaClassName = getJavaClassFqName(javaClass);
124            register(javaClassName, kotlinDescriptor);
125            registerCovariant(javaClassName, kotlinMutableDescriptor);
126        }
127    }
128
129    private void register(@NotNull FqName javaClassName, @NotNull ClassDescriptor kotlinDescriptor) {
130        classDescriptorMap.put(javaClassName, kotlinDescriptor);
131        packagesWithMappedClasses.put(javaClassName.parent(), kotlinDescriptor);
132    }
133
134    private void registerCovariant(@NotNull FqName javaClassName, @NotNull ClassDescriptor kotlinDescriptor) {
135        classDescriptorMapForCovariantPositions.put(javaClassName, kotlinDescriptor);
136        packagesWithMappedClasses.put(javaClassName.parent(), kotlinDescriptor);
137    }
138
139    @NotNull
140    public Collection<ClassDescriptor> mapPlatformClass(@NotNull FqName fqName) {
141        ClassDescriptor kotlinAnalog = classDescriptorMap.get(fqName);
142        ClassDescriptor kotlinCovariantAnalog = classDescriptorMapForCovariantPositions.get(fqName);
143        List<ClassDescriptor> descriptors = Lists.newArrayList();
144        if (kotlinAnalog != null) {
145            descriptors.add(kotlinAnalog);
146        }
147        if (kotlinCovariantAnalog != null) {
148            descriptors.add(kotlinCovariantAnalog);
149        }
150        return descriptors;
151    }
152
153    @Override
154    @NotNull
155    public Collection<ClassDescriptor> mapPlatformClass(@NotNull ClassDescriptor classDescriptor) {
156        FqNameUnsafe className = DescriptorUtils.getFQName(classDescriptor);
157        if (!className.isSafe()) {
158            return Collections.emptyList();
159        }
160        return mapPlatformClass(className.toSafe());
161    }
162
163    @Override
164    @NotNull
165    public Collection<ClassDescriptor> mapPlatformClassesInside(@NotNull DeclarationDescriptor containingDeclaration) {
166        FqNameUnsafe fqName = DescriptorUtils.getFQName(containingDeclaration);
167        if (!fqName.isSafe()) {
168            return Collections.emptyList();
169        }
170        return packagesWithMappedClasses.get(fqName.toSafe());
171    }
172}