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