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;
018    
019    import com.google.common.collect.*;
020    import com.intellij.psi.CommonClassNames;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.PlatformToKotlinClassMap;
024    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
025    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.name.FqName;
029    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
030    import org.jetbrains.jet.lang.types.JetType;
031    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
032    import org.jetbrains.jet.lang.types.lang.PrimitiveType;
033    
034    import java.util.*;
035    
036    public 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    }