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.openapi.util.Pair;
021import com.intellij.psi.PsiMethod;
022import com.intellij.psi.PsiSubstitutor;
023import com.intellij.psi.util.PsiFormatUtil;
024import org.jetbrains.annotations.NotNull;
025import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
027import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028import org.jetbrains.jet.lang.resolve.name.Name;
029import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
030import org.jetbrains.jet.renderer.DescriptorRenderer;
031
032import java.util.Collection;
033import java.util.List;
034import java.util.Map;
035import java.util.Set;
036
037import static com.intellij.psi.util.PsiFormatUtilBase.*;
038
039public class JavaToKotlinMethodMap {
040    public static final JavaToKotlinMethodMap INSTANCE = new JavaToKotlinMethodMap();
041
042    private final JavaToKotlinMethodMapGenerated mapContainer = new JavaToKotlinMethodMapGenerated();
043
044    private JavaToKotlinMethodMap() {
045    }
046
047    @NotNull
048    public List<FunctionDescriptor> getFunctions(@NotNull PsiMethod psiMethod, @NotNull ClassDescriptor containingClass) {
049        ImmutableCollection<ClassData> classDatas = mapContainer.map.get(psiMethod.getContainingClass().getQualifiedName());
050
051        List<FunctionDescriptor> result = Lists.newArrayList();
052
053        Set<ClassDescriptor> allSuperClasses = DescriptorUtils.getAllSuperClasses(containingClass);
054
055        String serializedPsiMethod = serializePsiMethod(psiMethod);
056        for (ClassData classData : classDatas) {
057            String expectedSerializedFunction = classData.method2Function.get(serializedPsiMethod);
058            if (expectedSerializedFunction == null) continue;
059
060            ClassDescriptor kotlinClass = classData.kotlinClass;
061            if (!allSuperClasses.contains(kotlinClass)) continue;
062
063
064            Collection<FunctionDescriptor> functions =
065                    kotlinClass.getDefaultType().getMemberScope().getFunctions(Name.identifier(psiMethod.getName()));
066
067            for (FunctionDescriptor function : functions) {
068                if (expectedSerializedFunction.equals(serializeFunction(function))) {
069                    result.add(function);
070                }
071            }
072        }
073
074        return result;
075    }
076
077    @NotNull
078    public static String serializePsiMethod(@NotNull PsiMethod psiMethod) {
079        return PsiFormatUtil.formatMethod(
080                psiMethod, PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES);
081    }
082
083    @NotNull
084    public static String serializeFunction(@NotNull FunctionDescriptor fun) {
085        return DescriptorRenderer.COMPACT.render(fun);
086    }
087
088    // used in generated code
089    static Pair<String, String> pair(String a, String b) {
090        return Pair.create(a, b);
091    }
092
093    // used in generated code
094    static void put(
095            ImmutableMultimap.Builder<String, ClassData> builder,
096            String javaFqName,
097            String kotlinQualifiedName,
098            Pair<String, String>... methods2Functions
099    ) {
100        ImmutableMap<String, String> methods2FunctionsMap = pairs2Map(methods2Functions);
101
102        ClassDescriptor kotlinClass;
103        if (kotlinQualifiedName.contains(".")) { // Map.Entry and MutableMap.MutableEntry
104            String[] kotlinNames = kotlinQualifiedName.split("\\.");
105            assert kotlinNames.length == 2 : "unexpected qualified name " + kotlinQualifiedName;
106
107            ClassDescriptor outerClass = KotlinBuiltIns.getInstance().getBuiltInClassByName(Name.identifier(kotlinNames[0]));
108            kotlinClass = DescriptorUtils.getInnerClassByName(outerClass, kotlinNames[1]);
109            assert kotlinClass != null : "Class not found: " + kotlinQualifiedName;
110        }
111        else {
112            kotlinClass = KotlinBuiltIns.getInstance().getBuiltInClassByName(Name.identifier(kotlinQualifiedName));
113        }
114
115        builder.put(javaFqName, new ClassData(kotlinClass, methods2FunctionsMap));
116    }
117
118    private static ImmutableMap<String, String> pairs2Map(Pair<String, String>[] pairs) {
119        ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
120        for (Pair<String, String> pair : pairs) {
121            builder.put(pair.first, pair.second);
122        }
123        return builder.build();
124    }
125
126    static class ClassData {
127        @NotNull
128        public final ClassDescriptor kotlinClass;
129        @NotNull
130        public Map<String, String> method2Function;
131
132        public ClassData(@NotNull ClassDescriptor kotlinClass, @NotNull Map<String, String> method2Function) {
133            this.kotlinClass = kotlinClass;
134            this.method2Function = method2Function;
135        }
136    }
137}