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