| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.jetbrains.plugins.groovy.gpp; |
| |
| import com.intellij.codeInsight.generation.OverrideImplementExploreUtil; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.psi.*; |
| import com.intellij.psi.infos.CandidateInfo; |
| import com.intellij.util.Function; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrListOrMap; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesProvider; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.AbstractClosureParameterEnhancer; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; |
| import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; |
| |
| import java.util.*; |
| |
| /** |
| * @author peter |
| */ |
| public class GppClosureParameterTypeProvider extends AbstractClosureParameterEnhancer { |
| @Override |
| protected PsiType getClosureParameterType(GrClosableBlock closure, int index) { |
| final PsiElement parent = closure.getParent(); |
| if (parent instanceof GrNamedArgument) { |
| final Pair<PsiMethod, PsiSubstitutor> pair = getOverriddenMethod((GrNamedArgument)parent); |
| if (pair != null) { |
| final PsiParameter[] parameters = pair.first.getParameterList().getParameters(); |
| if (parameters.length > index) { |
| return pair.second.substitute(parameters[index].getType()); |
| } |
| return null; |
| } |
| } |
| |
| if (parent instanceof GrListOrMap) { |
| final GrListOrMap list = (GrListOrMap)parent; |
| if (!list.isMap()) { |
| final PsiType listType = list.getType(); |
| final int argIndex = Arrays.asList(list.getInitializers()).indexOf(closure); |
| assert argIndex >= 0; |
| if (listType instanceof GrTupleType) { |
| for (PsiType type : GroovyExpectedTypesProvider.getDefaultExpectedTypes(list)) { |
| if (!(type instanceof PsiClassType)) continue; |
| |
| final GroovyResolveResult[] candidates = PsiUtil.getConstructorCandidates((PsiClassType)type,((GrTupleType)listType).getComponentTypes(),closure); |
| for (GroovyResolveResult resolveResult : candidates) { |
| final PsiElement method = resolveResult.getElement(); |
| if (!(method instanceof PsiMethod) || !((PsiMethod)method).isConstructor()) continue; |
| |
| final PsiParameter[] parameters = ((PsiMethod)method).getParameterList().getParameters(); |
| if (parameters.length <= argIndex) continue; |
| |
| final PsiType toCastTo = resolveResult.getSubstitutor().substitute(parameters[argIndex].getType()); |
| final PsiType suggestion = getSingleMethodParameterType(toCastTo, index, closure); |
| if (suggestion != null) return suggestion; |
| } |
| } |
| } |
| return null; |
| } |
| } |
| |
| for (PsiType constraint : GroovyExpectedTypesProvider.getDefaultExpectedTypes(closure)) { |
| final PsiType suggestion = getSingleMethodParameterType(constraint, index, closure); |
| if (suggestion != null) { |
| return suggestion; |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static Pair<PsiMethod, PsiSubstitutor> getOverriddenMethod(GrNamedArgument namedArgument) { |
| return ContainerUtil.getFirstItem(getOverriddenMethodVariants(namedArgument), null); |
| } |
| |
| @NotNull |
| public static List<Pair<PsiMethod, PsiSubstitutor>> getOverriddenMethodVariants(GrNamedArgument namedArgument) { |
| |
| final GrArgumentLabel label = namedArgument.getLabel(); |
| if (label == null) { |
| return Collections.emptyList(); |
| } |
| |
| final String methodName = label.getName(); |
| if (methodName == null) { |
| return Collections.emptyList(); |
| } |
| |
| final PsiElement map = namedArgument.getParent(); |
| if (map instanceof GrListOrMap && ((GrListOrMap)map).isMap()) { |
| for (PsiType expected : GroovyExpectedTypesProvider.getDefaultExpectedTypes((GrExpression)map)) { |
| if (expected instanceof PsiClassType) { |
| final List<Pair<PsiMethod, PsiSubstitutor>> pairs = getMethodsToOverrideImplementInInheritor((PsiClassType)expected, false); |
| return ContainerUtil.findAll(pairs, new Condition<Pair<PsiMethod, PsiSubstitutor>>() { |
| @Override |
| public boolean value(Pair<PsiMethod, PsiSubstitutor> pair) { |
| return methodName.equals(pair.first.getName()); |
| } |
| }); |
| } |
| } |
| } |
| |
| return Collections.emptyList(); |
| } |
| |
| @Nullable |
| public static PsiType getSingleMethodParameterType(@Nullable PsiType type, int index, GrClosableBlock closure) { |
| final PsiType[] signature = findSingleAbstractMethodSignature(type); |
| if (signature != null && GrClosureSignatureUtil.isSignatureApplicable(GrClosureSignatureUtil.createSignature(closure), signature, closure)) { |
| return signature.length > index ? signature[index] : PsiType.NULL; |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static PsiType[] findSingleAbstractMethodSignature(@Nullable PsiType type) { |
| if (type instanceof PsiClassType && !(TypesUtil.isClassType(type, GroovyCommonClassNames.GROOVY_LANG_CLOSURE))) { |
| List<Pair<PsiMethod, PsiSubstitutor>> result = getMethodsToOverrideImplementInInheritor((PsiClassType)type, true); |
| if (result.size() == 1) { |
| return getParameterTypes(result.get(0)); |
| } |
| } |
| return null; |
| } |
| |
| public static PsiType[] getParameterTypes(final Pair<PsiMethod, PsiSubstitutor> pair) { |
| return ContainerUtil.map2Array(pair.first.getParameterList().getParameters(), PsiType.class, new Function<PsiParameter, PsiType>() { |
| @Override |
| public PsiType fun(PsiParameter psiParameter) { |
| return pair.second.substitute(psiParameter.getType()); |
| } |
| }); |
| } |
| |
| @NotNull |
| public static List<Pair<PsiMethod, PsiSubstitutor>> getMethodsToOverrideImplementInInheritor(PsiClassType classType, boolean toImplement) { |
| final PsiClassType.ClassResolveResult resolveResult = classType.resolveGenerics(); |
| final PsiClass psiClass = resolveResult.getElement(); |
| if (psiClass == null) { |
| return Collections.emptyList(); |
| } |
| |
| List<Pair<PsiMethod, PsiSubstitutor>> over = getMethodsToOverrideImplement(psiClass, false); |
| List<Pair<PsiMethod, PsiSubstitutor>> impl = getMethodsToOverrideImplement(psiClass, true); |
| |
| for (PsiMethod method : psiClass.getMethods()) { |
| (method.hasModifierProperty(PsiModifier.ABSTRACT) ? impl : over).add(Pair.create(method, PsiSubstitutor.EMPTY)); |
| } |
| |
| for (Iterator<Pair<PsiMethod, PsiSubstitutor>> iterator = impl.iterator(); iterator.hasNext();) { |
| Pair<PsiMethod, PsiSubstitutor> pair = iterator.next(); |
| if (hasTraitImplementation(pair.first)) { |
| iterator.remove(); |
| over.add(pair); |
| } |
| } |
| |
| final List<Pair<PsiMethod, PsiSubstitutor>> result = toImplement ? impl : over; |
| for (int i = 0, resultSize = result.size(); i < resultSize; i++) { |
| Pair<PsiMethod, PsiSubstitutor> pair = result.get(i); |
| result.set(i, Pair.create(pair.first, resolveResult.getSubstitutor().putAll(pair.second))); |
| } |
| return result; |
| } |
| |
| private static ArrayList<Pair<PsiMethod, PsiSubstitutor>> getMethodsToOverrideImplement(PsiClass psiClass, final boolean toImplement) { |
| final ArrayList<Pair<PsiMethod, PsiSubstitutor>> result = new ArrayList<Pair<PsiMethod, PsiSubstitutor>>(); |
| for (CandidateInfo info : OverrideImplementExploreUtil.getMethodsToOverrideImplement(psiClass, toImplement)) { |
| result.add(Pair.create((PsiMethod) info.getElement(), info.getSubstitutor())); |
| } |
| return result; |
| } |
| |
| private static boolean hasTraitImplementation(PsiMethod method) { |
| return method.getModifierList().findAnnotation("org.mbte.groovypp.runtime.HasDefaultImplementation") != null; |
| } |
| } |