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.jet.lang.PlatformToKotlinClassMap;
022    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
023    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
024    import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
025    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
026    import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptorImpl;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.constants.StringValue;
029    import org.jetbrains.jet.lang.resolve.java.JvmPrimitiveType;
030    import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils;
031    import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage;
032    import org.jetbrains.jet.lang.resolve.name.FqName;
033    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
034    import org.jetbrains.jet.lang.types.JetType;
035    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
036    import org.jetbrains.jet.lang.types.lang.PrimitiveType;
037    
038    import java.util.*;
039    
040    import static org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME;
041    import static org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils.fqNameByClass;
042    
043    public class JavaToKotlinClassMap extends JavaToKotlinClassMapBuilder implements PlatformToKotlinClassMap {
044        public static final FqName JAVA_LANG_DEPRECATED = new FqName("java.lang.Deprecated");
045    
046        private static JavaToKotlinClassMap instance = null;
047    
048        @NotNull
049        public static JavaToKotlinClassMap getInstance() {
050            if (instance == null) {
051                instance = new JavaToKotlinClassMap();
052            }
053            return instance;
054        }
055    
056        private final Map<FqName, ClassDescriptor> classDescriptorMap = new HashMap<FqName, ClassDescriptor>();
057        private final Map<FqName, ClassDescriptor> classDescriptorMapForCovariantPositions = new HashMap<FqName, ClassDescriptor>();
058        private final Map<String, JetType> primitiveTypesMap = new HashMap<String, JetType>();
059        private final Map<FqName, Collection<ClassDescriptor>> packagesWithMappedClasses = new HashMap<FqName, Collection<ClassDescriptor>>();
060    
061        private JavaToKotlinClassMap() {
062            init();
063            initPrimitives();
064        }
065    
066        private void initPrimitives() {
067            KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
068            for (JvmPrimitiveType jvmPrimitiveType : JvmPrimitiveType.values()) {
069                PrimitiveType primitiveType = jvmPrimitiveType.getPrimitiveType();
070                String name = jvmPrimitiveType.getName();
071                FqName wrapperFqName = jvmPrimitiveType.getWrapperFqName();
072    
073                register(wrapperFqName, builtIns.getPrimitiveClassDescriptor(primitiveType));
074                primitiveTypesMap.put(name, builtIns.getPrimitiveJetType(primitiveType));
075                primitiveTypesMap.put("[" + name, builtIns.getPrimitiveArrayJetType(primitiveType));
076                primitiveTypesMap.put(wrapperFqName.asString(), builtIns.getNullablePrimitiveJetType(primitiveType));
077            }
078            primitiveTypesMap.put("void", KotlinBuiltIns.getInstance().getUnitType());
079        }
080    
081        @Nullable
082        public JetType mapPrimitiveKotlinClass(@NotNull String name) {
083            return primitiveTypesMap.get(name);
084        }
085    
086        @Nullable
087        public ClassDescriptor mapKotlinClass(@NotNull FqName fqName, @NotNull TypeUsage typeUsage) {
088            if (typeUsage == TypeUsage.MEMBER_SIGNATURE_COVARIANT
089                    || typeUsage == TypeUsage.SUPERTYPE) {
090                ClassDescriptor descriptor = classDescriptorMapForCovariantPositions.get(fqName);
091                if (descriptor != null) {
092                    return descriptor;
093                }
094            }
095            return classDescriptorMap.get(fqName);
096        }
097    
098        @Nullable
099        public AnnotationDescriptor mapToAnnotationClass(@NotNull FqName fqName) {
100            ClassDescriptor classDescriptor = classDescriptorMap.get(fqName);
101            if (classDescriptor != null && fqName.equals(JAVA_LANG_DEPRECATED)) {
102                return getAnnotationDescriptorForJavaLangDeprecated(classDescriptor);
103            }
104            return null;
105        }
106    
107        @NotNull
108        private static AnnotationDescriptor getAnnotationDescriptorForJavaLangDeprecated(@NotNull ClassDescriptor annotationClass) {
109            AnnotationDescriptorImpl annotation = new AnnotationDescriptorImpl();
110            annotation.setAnnotationType(annotationClass.getDefaultType());
111            ValueParameterDescriptor value =
112                    DescriptorResolverUtils.getAnnotationParameterByName(DEFAULT_ANNOTATION_MEMBER_NAME, annotationClass);
113            assert value != null : "kotlin.deprecated must have one parameter called value";
114            annotation.setValueArgument(value, new StringValue("Deprecated in Java", true));
115            return annotation;
116        }
117    
118        @Override
119        protected void register(@NotNull Class<?> javaClass, @NotNull ClassDescriptor kotlinDescriptor, @NotNull Direction direction) {
120            if (direction == Direction.BOTH || direction == Direction.JAVA_TO_KOTLIN) {
121                register(fqNameByClass(javaClass), kotlinDescriptor);
122            }
123        }
124    
125        @Override
126        protected void register(
127                @NotNull Class<?> javaClass,
128                @NotNull ClassDescriptor kotlinDescriptor,
129                @NotNull ClassDescriptor kotlinMutableDescriptor,
130                @NotNull Direction direction
131        ) {
132            if (direction == Direction.BOTH || direction == Direction.JAVA_TO_KOTLIN) {
133                FqName javaClassName = fqNameByClass(javaClass);
134                register(javaClassName, kotlinDescriptor);
135                registerCovariant(javaClassName, kotlinMutableDescriptor);
136            }
137        }
138    
139        private void register(@NotNull FqName javaClassName, @NotNull ClassDescriptor kotlinDescriptor) {
140            classDescriptorMap.put(javaClassName, kotlinDescriptor);
141            registerClassInPackage(javaClassName.parent(), kotlinDescriptor);
142        }
143    
144        private void registerCovariant(@NotNull FqName javaClassName, @NotNull ClassDescriptor kotlinDescriptor) {
145            classDescriptorMapForCovariantPositions.put(javaClassName, kotlinDescriptor);
146            registerClassInPackage(javaClassName.parent(), kotlinDescriptor);
147        }
148    
149        private void registerClassInPackage(@NotNull FqName packageFqName, @NotNull ClassDescriptor kotlinDescriptor) {
150            Collection<ClassDescriptor> classesInPackage = packagesWithMappedClasses.get(packageFqName);
151            if (classesInPackage == null) {
152                classesInPackage = new HashSet<ClassDescriptor>();
153                packagesWithMappedClasses.put(packageFqName, classesInPackage);
154            }
155            classesInPackage.add(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            Collection<ClassDescriptor> result = packagesWithMappedClasses.get(fqName.toSafe());
190            return result == null ? Collections.<ClassDescriptor>emptySet() : Collections.unmodifiableCollection(result);
191        }
192    }