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.openapi.util.Pair;
021    import com.intellij.psi.PsiMethod;
022    import com.intellij.psi.PsiSubstitutor;
023    import com.intellij.psi.util.PsiFormatUtil;
024    import org.jetbrains.annotations.NotNull;
025    import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
026    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
027    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
028    import org.jetbrains.jet.lang.resolve.name.Name;
029    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
030    import org.jetbrains.jet.renderer.DescriptorRenderer;
031    
032    import java.util.Collection;
033    import java.util.List;
034    import java.util.Map;
035    import java.util.Set;
036    
037    import static com.intellij.psi.util.PsiFormatUtilBase.*;
038    
039    public 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    }