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