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