grails controller, domain class implicit members resolve
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / statements / arguments / GrArgumentLabelImpl.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.arguments;
18
19 import com.intellij.lang.ASTNode;
20 import com.intellij.openapi.util.TextRange;
21 import com.intellij.psi.*;
22 import com.intellij.psi.util.PropertyUtil;
23 import com.intellij.util.ArrayUtil;
24 import com.intellij.util.IncorrectOperationException;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
27 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
28 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
29 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
30 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentLabel;
31 import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
32 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrCallExpression;
34 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
35 import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiElementImpl;
36 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
37 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
38 import org.jetbrains.plugins.groovy.lang.resolve.processors.PropertyResolverProcessor;
39
40 /**
41  * @author ilyas
42  */
43 public class GrArgumentLabelImpl extends GroovyPsiElementImpl implements GrArgumentLabel {
44
45   public GrArgumentLabelImpl(@NotNull ASTNode node) {
46     super(node);
47   }
48
49   public void accept(GroovyElementVisitor visitor) {
50     visitor.visitArgumentLabel(this);
51   }
52
53   public String toString() {
54     return "Argument label";
55   }
56
57   public PsiReference getReference() {
58     return this;
59   }
60
61   public String getName() {
62     return getNameElement().getText();
63   }
64
65   public PsiElement getElement() {
66     return this;
67   }
68
69   public TextRange getRangeInElement() {
70     return new TextRange(0, getTextLength());
71   }
72
73   @Nullable
74   public PsiElement resolve() {
75     String propName = getText();
76     String setterName = PropertyUtil.suggestSetterName(propName);
77     PsiElement context = getParent().getParent();
78     if (context instanceof GrArgumentList) {
79       final PsiElement parent = context.getParent();
80       if (parent instanceof GrCallExpression) {
81         final PsiMethod resolvedMethod = ((GrCallExpression) parent).resolveMethod();
82         if (resolvedMethod != null) {
83           final PsiParameter[] parameters = resolvedMethod.getParameterList().getParameters();
84           if (parameters.length > 0) {
85             if (PsiUtil.createMapType(resolvedMethod.getManager(), resolvedMethod.getResolveScope()).isAssignableFrom(parameters[0].getType())) {
86               //call with named argument, not setting property
87               return null;
88             }
89           }
90         }
91       }
92
93       if (parent instanceof GrExpression || parent instanceof GrAnonymousClassDefinition) {
94         PsiType type =
95           parent instanceof GrExpression ? ((GrExpression)parent).getType() : ((GrAnonymousClassDefinition)parent).getBaseClassType();
96         if (type instanceof PsiClassType) {
97           PsiClass clazz = ((PsiClassType) type).resolve();
98           if (clazz != null) {
99             PsiMethod[] byName = clazz.findMethodsByName(setterName, true);
100             if (byName.length > 0) return byName[0];
101             final PsiField field = clazz.findFieldByName(propName, true);
102             if (field != null) return field;
103             final PropertyResolverProcessor processor = new PropertyResolverProcessor(propName, this);
104             ResolveUtil
105               .processNonCodeMethods(JavaPsiFacade.getElementFactory(getProject()).createType(clazz), processor, getProject(), this, false);
106             final GroovyResolveResult[] candidates = processor.getCandidates();
107             if (candidates.length == 0) return null;
108             return candidates[0].getElement();
109           }
110         }
111       }
112     }
113     return null;
114   }
115
116   public String getCanonicalText() {
117     PsiElement resolved = resolve();
118     if (resolved instanceof PsiMember && resolved instanceof PsiNamedElement) {
119       PsiClass clazz = ((PsiMember) resolved).getContainingClass();
120       if (clazz != null) {
121         String qName = clazz.getQualifiedName();
122         if (qName != null) {
123           return qName + "." + ((PsiNamedElement) resolved).getName();
124         }
125       }
126     }
127
128     return getText();
129   }
130
131   public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
132     final PsiElement resolved = resolve();
133     if (resolved instanceof PsiMethod) {
134       final PsiMethod method = (PsiMethod) resolved;
135       final String oldName = getNameElement().getText();
136       if (!method.getName().equals(oldName)) { //was property reference to accessor
137         if (PropertyUtil.isSimplePropertySetter(method)) {
138           final String newPropertyName = PropertyUtil.getPropertyName(newElementName);
139           if (newPropertyName != null) {
140             return doHandleElementRename(newPropertyName);
141           } else {
142             //todo encapsulate fields:)
143           }
144         }
145       }
146     }
147     return doHandleElementRename(newElementName);
148   }
149
150   private PsiElement doHandleElementRename(String newElementName) {
151     PsiElement nameElement = getNameElement();
152     ASTNode node = nameElement.getNode();
153     ASTNode newNameNode = GroovyPsiElementFactory.getInstance(getProject()).createReferenceNameFromText(newElementName).getNode();
154     assert newNameNode != null && node != null;
155     node.getTreeParent().replaceChild(node, newNameNode);
156     return this;
157   }
158
159   public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
160     throw new IncorrectOperationException("NIY");
161   }
162
163   public boolean isReferenceTo(PsiElement element) {
164     return (element instanceof PsiMethod || element instanceof PsiField) &&
165         getManager().areElementsEquivalent(element, resolve());
166
167   }
168
169   public Object[] getVariants() {
170     return ArrayUtil.EMPTY_OBJECT_ARRAY;
171   }
172
173   public boolean isSoft() {
174     return false;
175   }
176
177   @NotNull
178   public PsiElement getNameElement() {
179     final PsiElement element = getFirstChild();
180     assert element != null;
181     return element;
182   }
183
184   @Nullable
185   public PsiType getExpectedArgumentType() {
186     final PsiElement resolved = resolve();
187     if (resolved instanceof PsiMethod) {
188       final PsiMethod method = (PsiMethod) resolved;
189       if (PropertyUtil.isSimplePropertyGetter(method))
190         return method.getReturnType();
191       if (PropertyUtil.isSimplePropertySetter(method))
192         return method.getParameterList().getParameters()[0].getType();
193
194     } else if (resolved instanceof PsiField) {
195       return ((PsiField) resolved).getType();
196     }
197
198     return null;
199   }
200 }