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