2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.jetbrains.plugins.groovy.lang.resolve.processors;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.pom.java.LanguageLevel;
21 import com.intellij.psi.*;
22 import com.intellij.psi.scope.JavaScopeProcessorEvent;
23 import com.intellij.psi.search.GlobalSearchScope;
24 import com.intellij.psi.util.PsiTreeUtil;
25 import org.jetbrains.annotations.Nullable;
26 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
27 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
28 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
29 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
30 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
31 import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnStatement;
32 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
34 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrGdkMethod;
35 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
36 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl;
37 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
38 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
39 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
46 public class MethodResolverProcessor extends ResolverProcessor {
47 private static final Logger LOG = Logger.getInstance("#org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor");
48 private final PsiType myThisType;
50 private PsiType[] myArgumentTypes;
51 private final PsiType[] myTypeArguments;
53 private final Set<GroovyResolveResult> myInapplicableCandidates = new LinkedHashSet<GroovyResolveResult>();
54 private final boolean myIsConstructor;
56 private boolean myStopExecuting = false;
58 public MethodResolverProcessor(String name, GroovyPsiElement place, boolean isConstructor, PsiType thisType, @Nullable PsiType[] argumentTypes, PsiType[] typeArguments) {
59 super(name, EnumSet.of(ResolveKind.METHOD, ResolveKind.PROPERTY), place, PsiType.EMPTY_ARRAY);
60 myIsConstructor = isConstructor;
61 myThisType = thisType;
62 myArgumentTypes = argumentTypes;
63 myTypeArguments = typeArguments;
66 public boolean execute(PsiElement element, ResolveState state) {
67 if (myStopExecuting) {
70 PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
71 if (element instanceof PsiMethod) {
72 PsiMethod method = (PsiMethod) element;
73 if (method.isConstructor() != myIsConstructor) return true;
74 if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
75 substitutor = obtainSubstitutor(substitutor, method);
76 boolean isAccessible = isAccessible(method);
77 boolean isStaticsOK = isStaticsOK(method);
78 if (PsiUtil.isApplicable(myArgumentTypes, method, substitutor, myCurrentFileResolveContext instanceof GrMethodCallExpression)) {
79 myCandidates.add(new GroovyResolveResultImpl(method, myCurrentFileResolveContext, substitutor, isAccessible, isStaticsOK));
81 myInapplicableCandidates.add(new GroovyResolveResultImpl(method, myCurrentFileResolveContext, substitutor, isAccessible, isStaticsOK));
85 } else if (element instanceof PsiVariable) {
86 if (element instanceof GrField && ((GrField) element).isProperty() ||
87 isClosure((PsiVariable) element)) {
88 return super.execute(element, state);
90 myInapplicableCandidates.add(new GroovyResolveResultImpl(element, myCurrentFileResolveContext, substitutor, isAccessible((PsiVariable)element), isStaticsOK((PsiVariable)element)));
98 private PsiSubstitutor obtainSubstitutor(PsiSubstitutor substitutor, PsiMethod method) {
99 final PsiTypeParameter[] typeParameters = method.getTypeParameters();
100 if (myTypeArguments.length == typeParameters.length) {
101 for (int i = 0; i < typeParameters.length; i++) {
102 PsiTypeParameter typeParameter = typeParameters[i];
103 final PsiType typeArgument = myTypeArguments[i];
104 substitutor = substitutor.put(typeParameter, typeArgument);
109 if (argumentsSupplied() && method.hasTypeParameters()) {
110 PsiType[] argTypes = myArgumentTypes;
111 if (method instanceof GrGdkMethod) {
112 assert argTypes != null;
113 //type inference should be performed from static method
114 PsiType[] newArgTypes = new PsiType[argTypes.length + 1];
115 newArgTypes[0] = myThisType;
116 System.arraycopy(argTypes, 0, newArgTypes, 1, argTypes.length);
117 argTypes = newArgTypes;
119 method = ((GrGdkMethod) method).getStaticMethod();
120 LOG.assertTrue(method.isValid());
122 return inferMethodTypeParameters(method, substitutor, typeParameters, argTypes);
128 private static boolean isClosure(PsiVariable variable) {
129 if (variable instanceof GrVariable) {
130 final PsiType type = ((GrVariable) variable).getTypeGroovy();
131 return type != null && type.equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE);
133 return variable.getType().equalsToText(GrClosableBlock.GROOVY_LANG_CLOSURE);
136 private PsiSubstitutor inferMethodTypeParameters(PsiMethod method, PsiSubstitutor partialSubstitutor, final PsiTypeParameter[] typeParameters, final PsiType[] argTypes) {
137 if (typeParameters.length == 0) return partialSubstitutor;
139 if (argumentsSupplied()) {
140 final PsiParameter[] parameters = method.getParameterList().getParameters();
141 final int max = Math.max(parameters.length, argTypes.length);
142 PsiType[] parameterTypes = new PsiType[max];
143 PsiType[] argumentTypes = new PsiType[max];
144 for (int i = 0; i < parameterTypes.length; i++) {
145 if (i < parameters.length) {
146 final PsiType type = parameters[i].getType();
147 if (argTypes.length == parameters.length &&
148 type instanceof PsiEllipsisType &&
149 !(argTypes[argTypes.length - 1] instanceof PsiArrayType)) {
150 parameterTypes[i] = ((PsiEllipsisType) type).getComponentType();
152 parameterTypes[i] = type;
155 if (parameters.length > 0) {
156 final PsiType lastParameterType = parameters[parameters.length - 1].getType();
157 if (argTypes.length > parameters.length && lastParameterType instanceof PsiEllipsisType) {
158 parameterTypes[i] = ((PsiEllipsisType) lastParameterType).getComponentType();
160 parameterTypes[i] = lastParameterType;
163 parameterTypes[i] = PsiType.NULL;
166 argumentTypes[i] = i < argTypes.length ? argTypes[i] : PsiType.NULL;
169 final PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
170 PsiSubstitutor substitutor = helper.inferTypeArguments(typeParameters, parameterTypes, argumentTypes, LanguageLevel.HIGHEST);
171 for (PsiTypeParameter typeParameter : typeParameters) {
172 if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
173 substitutor = inferFromContext(typeParameter, method.getReturnType(), substitutor, helper);
177 return partialSubstitutor.putAll(substitutor);
180 return partialSubstitutor;
183 private PsiSubstitutor inferFromContext(PsiTypeParameter typeParameter, PsiType lType, PsiSubstitutor substitutor, PsiResolveHelper helper) {
184 if (myPlace != null) {
185 final PsiType inferred = helper.getSubstitutionForTypeParameter(typeParameter, lType, getContextType(), false, LanguageLevel.HIGHEST);
186 if (inferred != PsiType.NULL) {
187 return substitutor.put(typeParameter, inferred);
194 private PsiType getContextType() {
195 final PsiElement parent = myPlace.getParent().getParent();
196 PsiType rType = null;
197 if (parent instanceof GrReturnStatement) {
198 final GrMethod method = PsiTreeUtil.getParentOfType(parent, GrMethod.class);
199 if (method != null) rType = method.getDeclaredReturnType();
201 else if (parent instanceof GrAssignmentExpression && myPlace.equals(((GrAssignmentExpression)parent).getRValue())) {
202 rType = ((GrAssignmentExpression)parent).getLValue().getType();
204 else if (parent instanceof GrVariable) {
205 rType = ((GrVariable)parent).getDeclaredType();
210 public GroovyResolveResult[] getCandidates() {
211 if (!myCandidates.isEmpty()) {
212 return filterCandidates();
214 if (!myInapplicableCandidates.isEmpty()) {
215 return ResolveUtil.filterSameSignatureCandidates(myInapplicableCandidates, myArgumentTypes != null ? myArgumentTypes.length : -1);
217 return GroovyResolveResult.EMPTY_ARRAY;
220 private GroovyResolveResult[] filterCandidates() {
221 GroovyResolveResult[] array = myCandidates.toArray(new GroovyResolveResult[myCandidates.size()]);
222 if (array.length == 1) return array;
224 List<GroovyResolveResult> result = new ArrayList<GroovyResolveResult>();
225 result.add(array[0]);
227 PsiManager manager = myPlace.getManager();
228 GlobalSearchScope scope = myPlace.getResolveScope();
230 boolean methodsPresent = array[0].getElement() instanceof PsiMethod;
231 boolean propertiesPresent = !methodsPresent;
233 for (int i = 1; i < array.length; i++) {
234 PsiElement currentElement = array[i].getElement();
235 if (currentElement instanceof PsiMethod) {
236 methodsPresent = true;
237 PsiMethod currentMethod = (PsiMethod) currentElement;
238 for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
239 final GroovyResolveResult otherResolveResult = iterator.next();
240 PsiElement element = otherResolveResult.getElement();
241 if (element instanceof PsiMethod) {
242 PsiMethod method = (PsiMethod) element;
243 if (dominated(currentMethod, array[i].getSubstitutor(), method, otherResolveResult.getSubstitutor(), manager, scope)) {
246 if (dominated(method, otherResolveResult.getSubstitutor(), currentMethod, array[i].getSubstitutor(), manager, scope)) {
252 propertiesPresent = true;
255 result.add(array[i]);
258 if (methodsPresent && propertiesPresent) {
259 for (Iterator<GroovyResolveResult> iterator = result.iterator(); iterator.hasNext();) {
260 GroovyResolveResult resolveResult = iterator.next();
261 if (!(resolveResult.getElement() instanceof PsiMethod)) iterator.remove();
265 return result.toArray(new GroovyResolveResult[result.size()]);
268 private boolean dominated(PsiMethod method1, PsiSubstitutor substitutor1, PsiMethod method2, PsiSubstitutor substitutor2, PsiManager manager, GlobalSearchScope scope) { //method1 has more general parameter types thn method2
269 if (!method1.getName().equals(method2.getName())) return false;
271 //hack for default gdk methods
272 if (method1 instanceof GrGdkMethod && method2 instanceof GrGdkMethod) {
273 method1 = ((GrGdkMethod)method1).getStaticMethod();
274 method2 = ((GrGdkMethod)method2).getStaticMethod();
276 PsiParameter[] params1 = method1.getParameterList().getParameters();
277 PsiParameter[] params2 = method2.getParameterList().getParameters();
278 if (myArgumentTypes == null && params1.length != params2.length) return false;
280 if (params1.length < params2.length) {
281 if (params1.length == 0) return false;
282 final PsiType lastType = params1[params1.length - 1].getType(); //varargs applicability
283 return lastType instanceof PsiArrayType;
286 for (int i = 0; i < params2.length; i++) {
287 PsiType type1 = substitutor1.substitute(params1[i].getType());
288 PsiType type2 = substitutor2.substitute(params2[i].getType());
289 if (!typesAgree(manager, scope, type1, type2)) return false;
295 private boolean typesAgree(PsiManager manager, GlobalSearchScope scope, PsiType type1, PsiType type2) {
296 if (argumentsSupplied() && type1 instanceof PsiArrayType && !(type2 instanceof PsiArrayType)) {
297 type1 = ((PsiArrayType) type1).getComponentType();
299 return argumentsSupplied() ? //resolve, otherwise same_name_variants
300 TypesUtil.isAssignable(type1, type2, manager, scope) :
304 private boolean argumentsSupplied() {
305 return myArgumentTypes != null;
309 public boolean hasCandidates() {
310 return super.hasCandidates() || !myInapplicableCandidates.isEmpty();
314 public PsiType[] getArgumentTypes() {
315 return myArgumentTypes;
318 public void setArgumentTypes(@Nullable PsiType[] argumentTypes) {
319 myArgumentTypes = argumentTypes;
323 public void handleEvent(Event event, Object associated) {
324 super.handleEvent(event, associated);
325 if (JavaScopeProcessorEvent.CHANGE_LEVEL == event && myCandidates.size() > 0) {
326 myStopExecuting = true;