67c37505ba77e8b43ddfb6968013c96d0d0d7fae
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / statements / expressions / GrReferenceExpressionImpl.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
18
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.util.Comparing;
22 import com.intellij.openapi.util.Computable;
23 import com.intellij.openapi.util.TextRange;
24 import com.intellij.openapi.util.text.StringUtil;
25 import com.intellij.psi.*;
26 import com.intellij.psi.impl.source.resolve.ResolveCache;
27 import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
28 import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
29 import com.intellij.psi.tree.IElementType;
30 import com.intellij.psi.util.PropertyUtil;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.psi.util.TypeConversionUtil;
33 import com.intellij.util.ArrayUtil;
34 import com.intellij.util.Consumer;
35 import com.intellij.util.Function;
36 import com.intellij.util.IncorrectOperationException;
37 import org.jetbrains.annotations.NonNls;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
41 import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
42 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
43 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
44 import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
45 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
46 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
49 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableBase;
50 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
51 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
52 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression;
53 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
54 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
55 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
56 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
57 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod;
58 import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
59 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList;
60 import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType;
61 import org.jetbrains.plugins.groovy.lang.psi.impl.GrReferenceElementImpl;
62 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
63 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
64 import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
65 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils;
66 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
67 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
68 import org.jetbrains.plugins.groovy.lang.resolve.processors.*;
69
70 import java.util.EnumSet;
71
72 /**
73  * @author ilyas
74  */
75 public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements GrReferenceExpression {
76   public GrReferenceExpressionImpl(@NotNull ASTNode node) {
77     super(node);
78   }
79
80   public void accept(GroovyElementVisitor visitor) {
81     visitor.visitReferenceExpression(this);
82   }
83
84   @Nullable
85   public PsiElement getReferenceNameElement() {
86     final ASTNode lastChild = getNode().getLastChildNode();
87     if (lastChild == null) return null;
88     for (IElementType elementType : TokenSets.REFERENCE_NAMES.getTypes()) {
89       if (lastChild.getElementType() == elementType) return lastChild.getPsi();
90     }
91
92     return null;
93   }
94
95   public PsiReference getReference() {
96     PsiReference[] otherReferences = ReferenceProvidersRegistry.getReferencesFromProviders(this, GrReferenceExpression.class);
97     PsiReference[] thisReference = {this};
98     return new PsiMultiReference(otherReferences.length == 0 ? thisReference : ArrayUtil.mergeArrays(thisReference, otherReferences, PsiReference.class), this);
99   }
100
101   @Nullable
102   public PsiElement getQualifier() {
103     return getQualifierExpression();
104   }
105
106   public String getReferenceName() {
107     PsiElement nameElement = getReferenceNameElement();
108     if (nameElement != null) {
109       if (nameElement.getNode().getElementType() == GroovyElementTypes.mSTRING_LITERAL ||
110           nameElement.getNode().getElementType() == GroovyElementTypes.mGSTRING_LITERAL) {
111         return GrStringUtil.removeQuotes(nameElement.getText());
112       }
113
114       return nameElement.getText();
115     }
116     return null;
117   }
118
119   public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
120     final PsiElement resolved = resolve();
121     if (resolved instanceof PsiMethod) {
122       final PsiMethod method = (PsiMethod) resolved;
123       final String oldName = getReferenceName();
124       if (!method.getName().equals(oldName)) { //was property reference to accessor
125         if (GroovyPropertyUtils.isSimplePropertyAccessor(method)) {
126           final String newPropertyName = PropertyUtil.getPropertyName(newElementName);
127           if (newPropertyName != null) {
128             return doHandleElementRename(newPropertyName);
129           } else {
130             //todo encapsulate fields:)
131           }
132         }
133       }
134     } else if (resolved instanceof GrField && ((GrField) resolved).isProperty()) {
135       final GrField field = (GrField) resolved;
136       final String oldName = getReferenceName();
137       if (oldName != null && !oldName.equals(field.getName())) { //was accessor reference to property
138         if (oldName.startsWith("get")) {
139           return doHandleElementRename("get" + StringUtil.capitalize(newElementName));
140         } else if (oldName.startsWith("set")) {
141           return doHandleElementRename("set" + StringUtil.capitalize(newElementName));
142         }
143       }
144     }
145
146     return doHandleElementRename(newElementName);
147   }
148
149   @Override
150   protected PsiElement bindWithQualifiedRef(String qName) {
151     final GrTypeArgumentList list = getTypeArgumentList();
152     final String typeArgs = (list != null) ? list.getText() : "";
153     final String text = qName + typeArgs;
154     GrReferenceExpression qualifiedRef = GroovyPsiElementFactory.getInstance(getProject()).createReferenceExpressionFromText(text);
155     getNode().getTreeParent().replaceChild(getNode(), qualifiedRef.getNode());
156     return qualifiedRef;
157   }
158
159   private PsiElement doHandleElementRename(String newElementName) throws IncorrectOperationException {
160     if (!PsiUtil.isValidReferenceName(newElementName)) {
161       PsiElement element = GroovyPsiElementFactory.getInstance(getProject()).createStringLiteral(newElementName);
162       getReferenceNameElement().replace(element);
163       return this;
164     }
165
166     return super.handleElementRename(newElementName);
167   }
168
169   public int getTextOffset() {
170     PsiElement parent = getParent();
171     TextRange range = getTextRange();
172     if (!(parent instanceof GrAssignmentExpression) || !this.equals(((GrAssignmentExpression) parent).getLValue())) {
173       return range.getEndOffset(); //need this as a hack against TargetElementUtil
174     }
175
176     return range.getStartOffset();
177   }
178
179   public String toString() {
180     return "Reference expression";
181   }
182
183   @Nullable
184   public PsiElement resolve() {
185     ResolveResult[] results = getManager().getResolveCache().resolveWithCaching(this, RESOLVER, true, false);
186     return results.length == 1 ? results[0].getElement() : null;
187   }
188
189   private static final OurResolver RESOLVER = new OurResolver();
190
191   private static final OurTypesCalculator TYPES_CALCULATOR = new OurTypesCalculator();
192
193   public PsiType getNominalType() {
194     return GroovyPsiManager.getInstance(getProject()).getTypeInferenceHelper().doWithInferenceDisabled(new Computable<PsiType>() {
195       public PsiType compute() {
196         return getNominalTypeImpl();
197       }
198     });
199   }
200
201   @Nullable
202   private PsiType getNominalTypeImpl() {
203     IElementType dotType = getDotTokenType();
204
205     final GroovyResolveResult resolveResult = advancedResolve();
206     PsiElement resolved = resolveResult.getElement();
207     if (dotType == GroovyTokenTypes.mMEMBER_POINTER) {
208       if (resolved instanceof PsiMethod) {
209         PsiMethod method = (PsiMethod) resolved;
210         PsiType returnType = resolveResult.getSubstitutor().substitute(method.getReturnType());
211         return GrClosureType.create(getResolveScope(), returnType, method.getParameterList().getParameters(), getManager());
212       }
213       return JavaPsiFacade.getInstance(getProject()).getElementFactory().createTypeByFQClassName(GrClosableBlock.GROOVY_LANG_CLOSURE, getResolveScope());
214     }
215     PsiType result = null;
216     JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
217     if (resolved == null && !"class".equals(getReferenceName())) {
218       resolved = getReference().resolve();
219     }
220     if (resolved instanceof PsiClass) {
221       if (getParent() instanceof GrReferenceExpression) {
222         result = facade.getElementFactory().createType((PsiClass) resolved);
223       } else {
224         PsiClass javaLangClass = facade.findClass("java.lang.Class", getResolveScope());
225         if (javaLangClass != null) {
226           PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
227           final PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
228           if (typeParameters.length == 1) {
229             substitutor = substitutor.put(typeParameters[0], facade.getElementFactory().createType((PsiClass) resolved));
230           }
231           result = facade.getElementFactory().createType(javaLangClass, substitutor);
232         }
233       }
234     } else if (resolved instanceof GrVariableBase) {
235       result = ((GrVariableBase) resolved).getDeclaredType();
236     } else if (resolved instanceof PsiVariable) {
237       result = ((PsiVariable) resolved).getType();
238     } else
239     if (resolved instanceof PsiMethod && !GroovyPsiManager.isTypeBeingInferred(resolved)) {
240       if (dotType == GroovyTokenTypes.mMEMBER_POINTER) {
241         return facade.getElementFactory().createTypeByFQClassName("groovy.lang.Closure", getResolveScope());
242       }
243       PsiMethod method = (PsiMethod) resolved;
244       if (PropertyUtil.isSimplePropertySetter(method) && !method.getName().equals(getReferenceName())) {
245         result = method.getParameterList().getParameters()[0].getType();
246       } else {
247         PsiClass containingClass = method.getContainingClass();
248         if (containingClass != null && "java.lang.Object".equals(containingClass.getQualifiedName()) &&
249                 "getClass".equals(method.getName())) {
250           result = getTypeForObjectGetClass(facade, method);
251         } else {
252           if (method instanceof GrAccessorMethod) {
253             result = ((GrAccessorMethod) method).getReturnTypeGroovy();
254           } else {
255             result = method.getReturnType();
256           }
257         }
258
259       }
260     } else if (resolved instanceof GrReferenceExpression) {
261       PsiElement parent = resolved.getParent();
262       if (parent instanceof GrAssignmentExpression) {
263         GrAssignmentExpression assignment = (GrAssignmentExpression) parent;
264         if (resolved.equals(assignment.getLValue())) {
265           GrExpression rValue = assignment.getRValue();
266           if (rValue != null) {
267             PsiType rType = rValue.getType();
268             if (rType != null) result = rType;
269           }
270         }
271       }
272     } else if (resolved == null) {
273       if ("class".equals(getReferenceName())) {
274         return JavaPsiFacade.getInstance(getProject()).getElementFactory().createTypeByFQClassName("java.lang.Class",
275                 getResolveScope());
276       }
277
278       GrExpression qualifier = getQualifierExpression();
279       if (qualifier != null) {
280         PsiType qType = qualifier.getType();
281         if (qType instanceof PsiClassType) {
282           PsiClassType.ClassResolveResult qResult = ((PsiClassType) qType).resolveGenerics();
283           PsiClass clazz = qResult.getElement();
284           if (clazz != null) {
285             PsiClass mapClass = facade.findClass("java.util.Map", getResolveScope());
286             if (mapClass != null && mapClass.getTypeParameters().length == 2) {
287               PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(mapClass, clazz, qResult.getSubstitutor());
288               if (substitutor != null) {
289                 return substitutor.substitute(mapClass.getTypeParameters()[1]);
290               }
291             }
292           }
293         }
294       }
295     }
296
297     if (result != null) {
298       result = resolveResult.getSubstitutor().substitute(result);
299       result = TypesUtil.boxPrimitiveType(result, getManager(), getResolveScope());
300     }
301     if (dotType != GroovyTokenTypes.mSPREAD_DOT) {
302       return result;
303     } else {
304       return ResolveUtil.getListTypeForSpreadOperator(this, result);
305     }
306   }
307
308   @Nullable
309   private PsiType getTypeForObjectGetClass(JavaPsiFacade facade, PsiMethod method) {
310     PsiType type = method.getReturnType();
311     if (type instanceof PsiClassType) {
312       PsiClass clazz = ((PsiClassType) type).resolve();
313       if (clazz != null &&
314               "java.lang.Class".equals(clazz.getQualifiedName())) {
315         PsiTypeParameter[] typeParameters = clazz.getTypeParameters();
316         if (typeParameters.length == 1) {
317           PsiClass qualifierClass = null;
318           GrExpression qualifier = getQualifierExpression();
319           if (qualifier != null) {
320             PsiType qualifierType = qualifier.getType();
321             if (qualifierType instanceof PsiClassType) {
322               qualifierClass = ((PsiClassType) qualifierType).resolve();
323             }
324           } else {
325             PsiNamedElement context = PsiTreeUtil.getParentOfType(this, PsiClass.class, GroovyFile.class);
326             if (context instanceof PsiClass) qualifierClass = (PsiClass) context;
327             else if (context instanceof GroovyFile) qualifierClass = ((GroovyFile) context).getScriptClass();
328           }
329
330           PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
331           if (qualifierClass != null) {
332             PsiType t = facade.getElementFactory().createType(qualifierClass);
333             substitutor = substitutor.put(typeParameters[0], t);
334           }
335           return facade.getElementFactory().createType(clazz, substitutor);
336         }
337       }
338     }
339     return type;
340   }
341
342   private static final class OurTypesCalculator implements Function<GrReferenceExpressionImpl, PsiType> {
343     public PsiType fun(GrReferenceExpressionImpl refExpr) {
344       final PsiType inferred = GroovyPsiManager.getInstance(refExpr.getProject()).getTypeInferenceHelper().getInferredType(refExpr);
345       final PsiType nominal = refExpr.getNominalTypeImpl();
346       if (inferred == null || PsiType.NULL.equals(inferred)) {
347         if (nominal == null) {
348           /*inside nested closure we could still try to infer from variable initializer.
349           * Not sound, but makes sense*/
350           final PsiElement resolved = refExpr.resolve();
351           if (resolved instanceof GrVariableBase) return ((GrVariableBase) resolved).getTypeGroovy();
352         }
353
354         return nominal;
355       }
356
357       if (nominal == null) return inferred;
358       if (!TypeConversionUtil.isAssignable(nominal, inferred, false)) {
359         final PsiElement resolved = refExpr.resolve();
360         if (resolved instanceof GrVariable && ((GrVariable) resolved).getTypeElementGroovy() != null) {
361           return nominal; //see GRVY-487
362         }
363       }
364       return inferred;
365     }
366   }
367
368   public PsiType getType() {
369     return GroovyPsiManager.getInstance(getProject()).getType(this, TYPES_CALCULATOR);
370   }
371
372   public GrExpression replaceWithExpression(@NotNull GrExpression newExpr, boolean removeUnnecessaryParentheses) {
373     return PsiImplUtil.replaceExpression(this, newExpr, removeUnnecessaryParentheses);
374   }
375
376   public String getName() {
377     return getReferenceName();
378   }
379
380   public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
381     PsiElement nameElement = getReferenceNameElement();
382     ASTNode node = nameElement.getNode();
383     ASTNode newNameNode = GroovyPsiElementFactory.getInstance(getProject()).createReferenceNameFromText(name).getNode();
384     assert newNameNode != null && node != null;
385     node.getTreeParent().replaceChild(node, newNameNode);
386
387     return this;
388   }
389
390   private static class OurResolver implements ResolveCache.PolyVariantResolver<GrReferenceExpressionImpl> {
391     public GroovyResolveResult[] resolve(GrReferenceExpressionImpl refExpr, boolean incompleteCode) {
392       String name = refExpr.getReferenceName();
393       if (name == null) return null;
394       ResolverProcessor processor = getMethodOrPropertyResolveProcessor(refExpr, name, incompleteCode);
395
396       resolveImpl(refExpr, processor);
397
398       GroovyResolveResult[] propertyCandidates = processor.getCandidates();
399       if (propertyCandidates.length > 0) return propertyCandidates;
400       if (refExpr.getKind() == Kind.TYPE_OR_PROPERTY) {
401         EnumSet<ClassHint.ResolveKind> kinds = refExpr.getParent() instanceof GrReferenceExpression ?
402                 EnumSet.of(ClassHint.ResolveKind.CLASS, ClassHint.ResolveKind.PACKAGE) :
403                 EnumSet.of(ClassHint.ResolveKind.CLASS);
404         ResolverProcessor classProcessor = new ClassResolverProcessor(refExpr.getReferenceName(), refExpr, kinds);
405         resolveImpl(refExpr, classProcessor);
406         return classProcessor.getCandidates();
407       }
408
409       return GroovyResolveResult.EMPTY_ARRAY;
410     }
411
412     private void resolveImpl(GrReferenceExpressionImpl refExpr, ResolverProcessor processor) {
413       GrExpression qualifier = refExpr.getQualifierExpression();
414       if (qualifier == null) {
415         ResolveUtil.treeWalkUp(refExpr, processor, true);
416         if (!processor.hasCandidates()) {
417           qualifier = PsiImplUtil.getRuntimeQualifier(refExpr);
418           if (qualifier != null) {
419             processQualifier(refExpr, processor, qualifier);
420           }
421         }
422       } else {
423         if (refExpr.getDotTokenType() != GroovyTokenTypes.mSPREAD_DOT) {
424           processQualifier(refExpr, processor, qualifier);
425         } else {
426           processQualifierForSpreadDot(refExpr, processor, qualifier);
427         }
428       }
429     }
430
431     private static void processQualifierForSpreadDot(GrReferenceExpressionImpl refExpr, ResolverProcessor processor, GrExpression qualifier) {
432       PsiType qualifierType = qualifier.getType();
433       if (qualifierType instanceof PsiClassType) {
434         PsiClassType.ClassResolveResult result = ((PsiClassType) qualifierType).resolveGenerics();
435         PsiClass clazz = result.getElement();
436         if (clazz != null) {
437           PsiClass listClass = ResolveUtil.findListClass(refExpr.getManager(), refExpr.getResolveScope());
438           if (listClass != null && listClass.getTypeParameters().length == 1) {
439             PsiSubstitutor substitutor = TypeConversionUtil.getClassSubstitutor(listClass, clazz, result.getSubstitutor());
440             if (substitutor != null) {
441               PsiType componentType = substitutor.substitute(listClass.getTypeParameters()[0]);
442               if (componentType != null) {
443                 processClassQualifierType(refExpr, processor, componentType);
444               }
445             }
446           }
447         }
448       } else if (qualifierType instanceof PsiArrayType) {
449         processClassQualifierType(refExpr, processor, ((PsiArrayType) qualifierType).getComponentType());
450       }
451     }
452
453     private static void processQualifier(GrReferenceExpressionImpl refExpr, ResolverProcessor processor, GrExpression qualifier) {
454       PsiType qualifierType = qualifier.getType();
455       if (qualifierType == null) {
456         if (qualifier instanceof GrReferenceExpression) {
457           PsiElement resolved = ((GrReferenceExpression) qualifier).resolve();
458           if (resolved instanceof PsiPackage) {
459             if (!resolved.processDeclarations(processor, ResolveState.initial(), null, refExpr)) //noinspection UnnecessaryReturnStatement
460               return;
461           }
462           else {
463             qualifierType = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory()
464               .createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT, refExpr.getResolveScope());
465             processClassQualifierType(refExpr, processor, qualifierType);
466           }
467         }
468       } else {
469         if (qualifierType instanceof PsiIntersectionType) {
470           for (PsiType conjunct : ((PsiIntersectionType) qualifierType).getConjuncts()) {
471             processClassQualifierType(refExpr, processor, conjunct);
472           }
473         } else {
474           processClassQualifierType(refExpr, processor, qualifierType);
475           if (qualifier instanceof GrReferenceExpression) {
476             PsiElement resolved = ((GrReferenceExpression) qualifier).resolve();
477             if (resolved instanceof PsiClass) { //omitted .class
478               PsiClass javaLangClass = PsiUtil.getJavaLangClass(resolved, refExpr.getResolveScope());
479               if (javaLangClass != null) {
480                 ResolveState state = ResolveState.initial();
481                 PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
482                 PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
483                 if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
484                 if (typeParameters.length == 1) {
485                   substitutor = substitutor.put(typeParameters[0], qualifierType);
486                   state = state.put(PsiSubstitutor.KEY, substitutor);
487                 }
488                 if (!javaLangClass.processDeclarations(processor, state, null, refExpr)) return;
489                 PsiType javaLangClassType = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(javaLangClass, substitutor);
490                 ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr.getProject(), refExpr, false);
491               }
492             }
493           }
494         }
495       }
496     }
497
498     private static void processClassQualifierType(GrReferenceExpressionImpl refExpr, ResolverProcessor processor, PsiType qualifierType) {
499       Project project = refExpr.getProject();
500       if (qualifierType instanceof PsiClassType) {
501         PsiClassType.ClassResolveResult qualifierResult = ((PsiClassType) qualifierType).resolveGenerics();
502         PsiClass qualifierClass = qualifierResult.getElement();
503         if (qualifierClass != null) {
504           if (!qualifierClass.processDeclarations(processor,
505                   ResolveState.initial().put(PsiSubstitutor.KEY, qualifierResult.getSubstitutor()), null, refExpr))
506             return;
507         }
508         if (!ResolveUtil.processCategoryMembers(refExpr, processor, (PsiClassType) qualifierType)) return;
509       } else if (qualifierType instanceof PsiArrayType) {
510         final GrTypeDefinition arrayClass = GroovyPsiManager.getInstance(project).getArrayClass();
511         if (!arrayClass.processDeclarations(processor, ResolveState.initial(), null, refExpr)) return;
512       } else if (qualifierType instanceof PsiIntersectionType) {
513         for (PsiType conjunct : ((PsiIntersectionType) qualifierType).getConjuncts()) {
514           processClassQualifierType(refExpr, processor, conjunct);
515         }
516         return;
517       }
518
519       ResolveUtil.processNonCodeMethods(qualifierType, processor, project, refExpr, false);
520     }
521   }
522
523   private static ResolverProcessor getMethodOrPropertyResolveProcessor(GrReferenceExpression refExpr, String name, boolean incomplete) {
524     if (incomplete) return CompletionProcessor.createRefSameNameProcessor(refExpr, name);
525
526     Kind kind = ((GrReferenceExpressionImpl) refExpr).getKind();
527     ResolverProcessor processor;
528     if (kind == Kind.METHOD_OR_PROPERTY) {
529       final PsiType[] argTypes = PsiUtil.getArgumentTypes(refExpr, false, false);
530       PsiType thisType = getThisType(refExpr);
531       processor = new MethodResolverProcessor(name, refExpr, false, thisType, argTypes, refExpr.getTypeArguments());
532     } else {
533       processor = new PropertyResolverProcessor(name, refExpr);
534     }
535
536     return processor;
537   }
538
539   private static PsiType getThisType(GrReferenceExpression refExpr) {
540     GrExpression qualifier = refExpr.getQualifierExpression();
541     if (qualifier != null) {
542       PsiType qType = qualifier.getType();
543       if (qType != null) return qType;
544     }
545
546     return TypesUtil.getJavaLangObject(refExpr);
547   }
548
549
550   enum Kind {
551     TYPE_OR_PROPERTY,
552     METHOD_OR_PROPERTY
553   }
554
555   Kind getKind() {
556     if (getDotTokenType() == GroovyTokenTypes.mMEMBER_POINTER) return Kind.METHOD_OR_PROPERTY;
557
558     PsiElement parent = getParent();
559     if (parent instanceof GrMethodCallExpression || parent instanceof GrApplicationStatement) {
560       return Kind.METHOD_OR_PROPERTY;
561     }
562
563     return Kind.TYPE_OR_PROPERTY;
564   }
565
566   @Nullable
567   public String getCanonicalText() {
568     return null;
569   }
570
571   public boolean isReferenceTo(PsiElement element) {
572     if (element instanceof PsiMethod && GroovyPropertyUtils.isSimplePropertyAccessor((PsiMethod) element)) {
573       final PsiElement target = resolve();
574       if (element instanceof GrAccessorMethod && getManager().areElementsEquivalent(((GrAccessorMethod)element).getProperty(), target)) {
575         return true;
576       }
577
578       return getManager().areElementsEquivalent(element, target);
579     }
580
581     if (element instanceof GrField && ((GrField) element).isProperty()) {
582       final PsiElement target = resolve();
583       if (getManager().areElementsEquivalent(element, target)) {
584         return true;
585       }
586
587       for (final GrAccessorMethod getter : ((GrField)element).getGetters()) {
588         if (getManager().areElementsEquivalent(getter, target)) {
589           return true;
590         }
591       }
592       return getManager().areElementsEquivalent(((GrField)element).getSetter(), target);
593     }
594
595     if (element instanceof PsiNamedElement && Comparing.equal(((PsiNamedElement) element).getName(), getReferenceName())) {
596       return getManager().areElementsEquivalent(element, resolve());
597     }
598     return false;
599   }
600
601   @NotNull
602   public Object[] getVariants() {
603     return ArrayUtil.EMPTY_OBJECT_ARRAY;
604   }
605
606
607   public boolean isSoft() {
608     return false;
609   }
610
611   public GrExpression getQualifierExpression() {
612     return findChildByClass(GrExpression.class);
613   }
614
615   public boolean isQualified() {
616     return getQualifierExpression() != null;
617   }
618
619   @Nullable
620   public PsiElement getDotToken() {
621     return findChildByType(GroovyTokenTypes.DOTS);
622   }
623
624   public void replaceDotToken(PsiElement newDot) {
625     if (newDot == null) return;
626     if (!GroovyTokenTypes.DOTS.contains(newDot.getNode().getElementType())) return;
627     final PsiElement oldDot = getDotToken();
628     if (oldDot == null) return;
629
630     getNode().replaceChild(oldDot.getNode(), newDot.getNode());
631   }
632
633   @Nullable
634   public IElementType getDotTokenType() {
635     PsiElement dot = getDotToken();
636     return dot == null ? null : dot.getNode().getElementType();
637   }
638
639   public GroovyResolveResult advancedResolve() {
640     ResolveResult[] results = getManager().getResolveCache().resolveWithCaching(this, RESOLVER, false, false);
641     return results.length == 1 ? (GroovyResolveResult) results[0] : GroovyResolveResult.EMPTY_RESULT;
642   }
643
644   @NotNull
645   public GroovyResolveResult[] multiResolve(boolean incomplete) {  //incomplete means we do not take arguments into consideration
646     return (GroovyResolveResult[]) getManager().getResolveCache().resolveWithCaching(this, RESOLVER, false, incomplete);
647   }
648
649   public void processVariants(Consumer<Object> consumer) {
650     CompleteReferenceExpression.processVariants(consumer, this);
651   }
652
653   @NotNull
654   public GroovyResolveResult[] getSameNameVariants() {
655     return RESOLVER.resolve(this, true);
656   }
657
658   public void setQualifierExpression(GrReferenceExpression newQualifier) {
659     final GrExpression oldQualifier = getQualifierExpression();
660     final ASTNode node = getNode();
661     final PsiElement refNameElement = getReferenceNameElement();
662     if (newQualifier == null) {
663       if (oldQualifier != null) {
664         if (refNameElement != null) {
665           node.removeRange(node.getFirstChildNode(), refNameElement.getNode());
666         }
667       }
668     } else {
669       if (oldQualifier != null) {
670         node.replaceChild(oldQualifier.getNode(), newQualifier.getNode());
671       } else {
672         if (refNameElement != null) {
673           node.addChild(newQualifier.getNode(), refNameElement.getNode());
674           node.addLeaf(GroovyTokenTypes.mDOT, ".", refNameElement.getNode());
675         }
676       }
677     }
678
679   }
680
681   public GrReferenceExpression bindToElementViaStaticImport(@NotNull PsiClass qualifierClass) {
682     if (getQualifier() != null) {
683       throw new IncorrectOperationException("Reference has qualifier");
684     }
685
686     if (StringUtil.isEmpty(getReferenceName())) {
687       throw new IncorrectOperationException("Reference has empty name");
688     }
689     final PsiFile file = getContainingFile();
690     if (file instanceof GroovyFile) {
691       final GrImportStatement statement = GroovyPsiElementFactory.getInstance(getProject())
692         .createImportStatementFromText("import static " + qualifierClass.getQualifiedName() + "." + getReferenceName());
693       ((GroovyFile)file).addImport(statement);
694     }
695     return this;
696   }
697 }