Don't use regexp matching for such a simple case.
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / statements / params / GrParameterImpl.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.params;
18
19 import com.intellij.lang.ASTNode;
20 import com.intellij.psi.*;
21 import com.intellij.psi.search.LocalSearchScope;
22 import com.intellij.psi.search.SearchScope;
23 import com.intellij.psi.util.InheritanceUtil;
24 import com.intellij.psi.util.PsiTreeUtil;
25 import com.intellij.psi.util.PsiUtil;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28 import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocComment;
29 import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocCommentOwner;
30 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
31 import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
32 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList;
33 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrCatchClause;
34 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParametersOwner;
35 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
36 import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrForInClause;
37 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
38 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrParenthesizedExpression;
39 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
40 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.arithmetic.GrRangeExpression;
41 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
45 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeElement;
46 import org.jetbrains.plugins.groovy.lang.psi.impl.GrTupleType;
47 import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
48 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.GrVariableImpl;
49 import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
50
51 /**
52  * @author: Dmitry.Krasilschikov
53  * @date: 26.03.2007
54  */
55 public class GrParameterImpl extends GrVariableImpl implements GrParameter {
56   public GrParameterImpl(@NotNull ASTNode node) {
57     super(node);
58   }
59
60   public void accept(GroovyElementVisitor visitor) {
61     visitor.visitParameter(this);
62   }
63
64   public String toString() {
65     return "Parameter";
66   }
67
68   @Nullable
69   public PsiType getTypeGroovy() {
70     GrTypeElement typeElement = getTypeElementGroovy();
71     if (typeElement != null) {
72       PsiType type = typeElement.getType();
73       if (!isVarArgs()) {
74         return type;
75       }
76       else {
77         return new PsiEllipsisType(type);
78       }
79     }
80     PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
81     if (isVarArgs()) {
82       PsiClassType type = factory.createTypeByFQClassName("java.lang.Object", getResolveScope());
83       return new PsiEllipsisType(type);
84     }
85     PsiElement parent = getParent();
86     if (parent instanceof GrForInClause) {
87       GrExpression iteratedExpression = ((GrForInClause)parent).getIteratedExpression();
88       if (iteratedExpression instanceof GrRangeExpression) {
89         return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_INTEGER, getResolveScope());
90       }
91       else if (iteratedExpression != null) {
92         PsiType result = findTypeForCollection(iteratedExpression, factory, this);
93         if (result != null) {
94           return result;
95         }
96       }
97     } else if (parent instanceof GrCatchClause) {
98       return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_THROWABLE, getResolveScope()); 
99     }
100
101     String argumentName = getElementToCompare();
102     GrClosableBlock closure = findClosureWithArgument(parent);
103
104     return findClosureParameterType(closure, argumentName, factory, this);
105   }
106
107   @Nullable
108   public static PsiType findClosureParameterType(GrClosableBlock closure,
109                                                  String argumentName,
110                                                  PsiElementFactory factory,
111                                                  PsiElement context) {
112     if (closure != null && closure.getParent() instanceof GrMethodCallExpression) {
113       GrMethodCallExpression methodCall = (GrMethodCallExpression)closure.getParent();
114       String methodName = findMethodName(methodCall);
115       //final GrExpression invokedExpression = methodCall.getInvokedExpression();
116       //PsiType type = findQualifierType(methodCall);
117
118       GrExpression expression = methodCall.getInvokedExpression();
119       if (!(expression instanceof GrReferenceExpression)) return null;
120
121       GrExpression qualifier = ((GrReferenceExpression)expression).getQualifierExpression();
122       if (qualifier == null) return null;
123       PsiType type = qualifier.getType();
124
125       GrParameter[] params = closure.getParameters();
126       if (type == null) {
127         return null;
128       }
129
130       if ("each".equals(methodName) ||
131           "every".equals(methodName) ||
132           "collect".equals(methodName) ||
133           "find".equals(methodName) ||
134           "findAll".equals(methodName) ||
135           "findIndexOf".equals(methodName)) {
136         PsiType res = findTypeForCollection(qualifier, factory, context);
137         if (closure.getParameters().length <= 1 && res != null) {
138           return res;
139         }
140
141         if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
142           if (closure.getParameters().length <= 1) {
143             return getEntryForMap(type, factory, context);
144           }
145           if (closure.getParameters().length == 2) {
146             if (argumentName.equals(params[0].getName())) {
147               return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, true);
148             }
149             return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 1, true);
150           }
151         }
152       }
153       else if ("with".equals(methodName) && closure.getParameters().length <= 1) {
154         return type;
155       }
156       else if ("eachWithIndex".equals(methodName)) {
157         PsiType res = findTypeForCollection(qualifier, factory, context);
158         if (closure.getParameters().length == 2 && res != null) {
159           if (argumentName.equals(params[0].getName())) {
160             return res;
161           }
162           return factory.createTypeFromText(CommonClassNames.JAVA_LANG_INTEGER, context);
163         }
164         if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
165           if (params.length == 2) {
166             if (argumentName.equals(params[0].getName())) {
167               return getEntryForMap(type, factory, context);
168             }
169             return factory.createTypeFromText(CommonClassNames.JAVA_LANG_INTEGER, context);
170           }
171           if (params.length == 3) {
172             if (argumentName.equals(params[0].getName())) {
173               return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 0, true);
174             }
175             if (argumentName.equals(params[1].getName())) {
176               return PsiUtil.substituteTypeParameter(type, CommonClassNames.JAVA_UTIL_MAP, 1, true);
177             }
178             return factory.createTypeFromText(CommonClassNames.JAVA_LANG_INTEGER, context);
179           }
180         }
181       }
182       else if ("inject".equals(methodName) && params.length == 2) {
183         if (argumentName.equals(params[0].getName())) {
184           return factory.createTypeFromText(CommonClassNames.JAVA_LANG_OBJECT, context);
185         }
186
187         PsiType res = findTypeForCollection(qualifier, factory, context);
188         if (res != null) {
189           return res;
190         }
191         if (InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP)) {
192           return getEntryForMap(type, factory, context);
193         }
194       }
195     }
196     return null;
197   }
198
199   @Nullable
200   private static PsiType getEntryForMap(PsiType map, PsiElementFactory factory, PsiElement context) {
201     PsiType key = PsiUtil.substituteTypeParameter(map, CommonClassNames.JAVA_UTIL_MAP, 0, true);
202     PsiType value = PsiUtil.substituteTypeParameter(map, CommonClassNames.JAVA_UTIL_MAP, 1, true);
203     if (key != null && value != null) {
204       return factory.createTypeFromText("java.util.Map.Entry<" + key.getCanonicalText() + ", " + value.getCanonicalText() + ">", context);
205     }
206     return null;
207   }
208
209   @Nullable
210   private static PsiType findTypeForCollection(GrExpression qualifier, PsiElementFactory factory, PsiElement context) {
211     PsiType iterType = qualifier.getType();
212     if (iterType == null) return null;
213     if (iterType instanceof PsiArrayType) {
214       return ((PsiArrayType)iterType).getComponentType();
215     }
216     if (iterType instanceof GrTupleType) {
217       PsiType[] types = ((GrTupleType)iterType).getParameters();
218       return types.length == 1 ? types[0] : null;
219     }
220
221     if (factory.createTypeFromText("groovy.lang.IntRange", context).isAssignableFrom(iterType)) {
222       return factory.createTypeFromText(CommonClassNames.JAVA_LANG_INTEGER, context);
223     }
224     if (factory.createTypeFromText("groovy.lang.ObjectRange", context).isAssignableFrom(iterType)) {
225       PsiElement element = qualifier;
226       element = removeBrackets(element);
227       if (element instanceof GrReferenceExpression) {
228         GrReferenceExpression ref = (GrReferenceExpression)element;
229         element = removeBrackets(ref.resolve());
230       }
231       if (element instanceof GrRangeExpression) {
232         return getRangeElementType((GrRangeExpression)element);
233       }
234       return null;
235     }
236
237     PsiType res = PsiUtil.extractIterableTypeParameter(iterType, true);
238     if (res != null) {
239       return res;
240     }
241
242     if (iterType.equalsToText(CommonClassNames.JAVA_LANG_STRING) || iterType.equalsToText("java.io.File")) {
243       return factory.createTypeFromText(CommonClassNames.JAVA_LANG_STRING, context);
244     }
245     return null;
246   }
247
248   private static PsiElement removeBrackets(PsiElement element) {
249     while (element instanceof GrParenthesizedExpression) {
250       element = ((GrParenthesizedExpression)element).getOperand();
251     }
252     return element;
253   }
254
255   @Nullable
256   private static PsiType getRangeElementType(GrRangeExpression range) {
257     GrExpression left = range.getLeftOperand();
258     GrExpression right = range.getRightOperand();
259     if (right != null) {
260       final PsiType leftType = left.getType();
261       final PsiType rightType = right.getType();
262       if (leftType != null && rightType != null) {
263         return TypesUtil.getLeastUpperBound(leftType, rightType, range.getManager());
264       }
265     }
266     return null;
267   }
268
269   @Nullable
270   private static String findMethodName(@NotNull GrMethodCallExpression methodCall) {
271     GrExpression expression = methodCall.getInvokedExpression();
272     if (expression instanceof GrReferenceExpression) {
273       return ((GrReferenceExpression)expression).getReferenceName();
274     }
275     return null;
276   }
277
278   @Nullable
279   private static GrClosableBlock findClosureWithArgument(@NotNull PsiElement parent) {
280     if (parent instanceof GrParameterList) {
281       GrParameterList list = (GrParameterList)parent;
282       if (list.getParent() instanceof GrClosableBlock) {
283         return (GrClosableBlock)list.getParent();
284       }
285     }
286     return null;
287   }
288
289   @NotNull
290   public PsiType getType() {
291     /*PsiType type = getTypeGroovy();
292     if (type == null) type = super.getType();*/
293     PsiType type = super.getType();
294     if (isVarArgs()) {
295       return new PsiEllipsisType(type);
296     }
297     else if (isMainMethodFirstUntypedParameter()) {
298       PsiClassType stringType =
299         JavaPsiFacade.getInstance(getProject()).getElementFactory().createTypeByFQClassName("java.lang.String", getResolveScope());
300       return stringType.createArrayType();
301     }
302     else {
303       return type;
304     }
305   }
306
307   private boolean isMainMethodFirstUntypedParameter() {
308     if (getTypeElementGroovy() != null) return false;
309
310     if (getParent() instanceof GrParameterList) {
311       GrParameterList parameterList = (GrParameterList)getParent();
312       GrParameter[] params = parameterList.getParameters();
313       if (params.length != 1 || this != params[0]) return false;
314
315       if (parameterList.getParent() instanceof GrMethod) {
316         GrMethod method = (GrMethod)parameterList.getParent();
317         return PsiImplUtil.isMainMethod(method);
318       }
319     }
320     return false;
321   }
322
323   public void setType(@Nullable PsiType type) {
324     throw new RuntimeException("NIY");
325   }
326
327   @Nullable
328   public GrTypeElement getTypeElementGroovy() {
329     return findChildByClass(GrTypeElement.class);
330   }
331
332   @Nullable
333   public GrExpression getDefaultInitializer() {
334     return findChildByClass(GrExpression.class);
335   }
336
337   public boolean isOptional() {
338     return getDefaultInitializer() != null;
339   }
340
341   @NotNull
342   public SearchScope getUseScope() {
343     if (!isPhysical()) {
344       final PsiFile file = getContainingFile();
345       final PsiElement context = file.getContext();
346       if (context != null) return new LocalSearchScope(context);
347       return super.getUseScope();
348     }
349
350     final PsiElement scope = getDeclarationScope();
351     if (scope instanceof GrDocCommentOwner) {
352       GrDocCommentOwner owner = (GrDocCommentOwner)scope;
353       final GrDocComment comment = owner.getDocComment();
354       if (comment != null) {
355         return new LocalSearchScope(new PsiElement[]{scope, comment});
356       }
357     }
358
359     return new LocalSearchScope(scope);
360   }
361
362   @NotNull
363   public String getName() {
364     return getNameIdentifierGroovy().getText();
365   }
366
367   public int getTextOffset() {
368     return getNameIdentifierGroovy().getTextRange().getStartOffset();
369   }
370
371   @Nullable
372   public GrModifierList getModifierList() {
373     return findChildByClass(GrModifierList.class);
374   }
375
376   @NotNull
377   public PsiElement getDeclarationScope() {
378     final GrParametersOwner owner = PsiTreeUtil.getParentOfType(this, GrParametersOwner.class);
379     assert owner != null;
380     if (owner instanceof GrForInClause) return owner.getParent();
381     return owner;
382   }
383
384   public boolean isVarArgs() {
385     PsiElement dots = findChildByType(GroovyTokenTypes.mTRIPLE_DOT);
386     return dots != null;
387   }
388
389   @NotNull
390   public PsiAnnotation[] getAnnotations() {
391     return PsiAnnotation.EMPTY_ARRAY;
392   }
393
394   @Override
395   public PsiType getDeclaredType() {
396     PsiType type = super.getDeclaredType();
397     if (type == null) {
398       type = getTypeGroovy();
399     }
400     return type;
401   }
402 }