| /* |
| * 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.lang.psi.typeEnhancers; |
| |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.psi.*; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.config.GroovyConfigUtils; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.GrAnnotationUtil; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; |
| import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; |
| |
| import java.util.Collections; |
| import java.util.List; |
| |
| /** |
| * Created by Max Medvedev on 27/02/14 |
| */ |
| public class ClosureParamsEnhancer extends AbstractClosureParameterEnhancer { |
| |
| @Nullable |
| @Override |
| protected PsiType getClosureParameterType(GrClosableBlock closure, int index) { |
| if (!GroovyConfigUtils.getInstance().isVersionAtLeast(closure, GroovyConfigUtils.GROOVY2_3)) return null; |
| |
| final GrParameter[] parameters = closure.getAllParameters(); |
| if (containsParametersWithDeclaredType(parameters)) { |
| return null; |
| } |
| |
| List<PsiType[]> fittingSignatures = findFittingSignatures(closure); |
| |
| if (fittingSignatures.size() == 1) { |
| PsiType[] expectedSignature = fittingSignatures.get(0); |
| return expectedSignature[index]; |
| } |
| |
| return null; |
| } |
| |
| @NotNull |
| public static List<PsiType[]> findFittingSignatures(GrClosableBlock closure) { |
| GrCall call = findCall(closure); |
| if (call == null) return Collections.emptyList(); |
| |
| List<PsiType[]> expectedSignatures = ContainerUtil.newArrayList(); |
| |
| GroovyResolveResult[] variants = call.getCallVariants(closure); |
| for (GroovyResolveResult variant : variants) { |
| expectedSignatures.addAll(inferExpectedSignatures(variant, call, closure)); |
| } |
| |
| final GrParameter[] parameters = closure.getAllParameters(); |
| return ContainerUtil.findAll(expectedSignatures, new Condition<PsiType[]>() { |
| @Override |
| public boolean value(PsiType[] types) { |
| return types.length == parameters.length; |
| } |
| }); |
| } |
| |
| private static List<PsiType[]> inferExpectedSignatures(@NotNull GroovyResolveResult variant, @NotNull GrCall call, @NotNull GrClosableBlock closure) { |
| PsiElement element = variant.getElement(); |
| |
| while (element instanceof PsiMirrorElement) element = ((PsiMirrorElement)element).getPrototype(); |
| if (!(element instanceof PsiMethod)) return Collections.emptyList(); |
| |
| List<Pair<PsiParameter, PsiType>> params = ResolveUtil.collectExpectedParamsByArg(closure, |
| new GroovyResolveResult[]{variant}, |
| call.getNamedArguments(), |
| call.getExpressionArguments(), |
| call.getClosureArguments(), closure); |
| if (params.isEmpty()) return Collections.emptyList(); |
| |
| Pair<PsiParameter, PsiType> pair = params.get(0); |
| |
| PsiParameter param = pair.getFirst(); |
| PsiModifierList modifierList = param.getModifierList(); |
| if (modifierList == null) return Collections.emptyList(); |
| |
| PsiAnnotation anno = modifierList.findAnnotation(GroovyCommonClassNames.GROOVY_TRANSFORM_STC_CLOSURE_PARAMS); |
| if (anno == null) return Collections.emptyList(); |
| |
| PsiClass closureSignatureHint = GrAnnotationUtil.inferClassAttribute(anno, "value"); |
| if (closureSignatureHint == null) return Collections.emptyList(); |
| |
| String qnameOfClosureSignatureHint = closureSignatureHint.getQualifiedName(); |
| if (qnameOfClosureSignatureHint == null) return Collections.emptyList(); |
| |
| SignatureHintProcessor signatureHintProcessor = SignatureHintProcessor.getHintProcessor(qnameOfClosureSignatureHint); |
| if (signatureHintProcessor == null) return Collections.emptyList(); |
| |
| return signatureHintProcessor.inferExpectedSignatures((PsiMethod)element, |
| variant.getSubstitutor(), |
| SignatureHintProcessor.buildOptions(anno)); |
| } |
| |
| private static boolean containsParametersWithDeclaredType(GrParameter[] parameters) { |
| return ContainerUtil.find(parameters, new Condition<GrParameter>() { |
| @Override |
| public boolean value(GrParameter parameter) { |
| return parameter.getDeclaredType() != null; |
| } |
| }) != null; |
| } |
| |
| @Nullable |
| private static GrCall findCall(@NotNull GrClosableBlock closure) { |
| PsiElement parent = closure.getParent(); |
| if (parent instanceof GrCall && ArrayUtil.contains(closure, ((GrCall)parent).getClosureArguments())) { |
| return (GrCall)parent; |
| } |
| |
| if (parent instanceof GrArgumentList) { |
| PsiElement pparent = parent.getParent(); |
| if (pparent instanceof GrCall) { |
| return (GrCall)pparent; |
| } |
| } |
| |
| return null; |
| } |
| } |