type inference for closures in groovy1.8
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / lang / psi / impl / GrClosureType.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;
18
19 import com.intellij.openapi.util.Comparing;
20 import com.intellij.pom.java.LanguageLevel;
21 import com.intellij.psi.*;
22 import com.intellij.psi.impl.PsiSubstitutorImpl;
23 import com.intellij.psi.search.GlobalSearchScope;
24 import com.intellij.util.containers.HashMap;
25 import org.jetbrains.annotations.NonNls;
26 import org.jetbrains.annotations.NotNull;
27 import org.jetbrains.annotations.Nullable;
28 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
29 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
30 import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature;
31 import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil;
32
33 import java.util.Map;
34
35 /**
36  * @author ven
37  */
38 public class GrClosureType extends PsiClassType {
39   private final GlobalSearchScope myScope;
40   private final PsiManager myManager;
41   private final @NotNull GrClosureSignature mySignature;
42   private final PsiType[] myTypeArgs;
43
44   private GrClosureType(LanguageLevel languageLevel, GlobalSearchScope scope, PsiManager manager, @NotNull GrClosureSignature closureSignature) {
45     super(languageLevel);
46     myScope = scope;
47     myManager = manager;
48     mySignature = closureSignature;
49     final PsiClass psiClass = resolve();
50     if (psiClass!=null && psiClass.getTypeParameters().length==1) {
51       myTypeArgs = new PsiType[]{mySignature.getReturnType()};
52     }
53     else {
54       myTypeArgs = PsiType.EMPTY_ARRAY;
55     }
56   }
57
58   @Nullable
59   public PsiClass resolve() {
60     return JavaPsiFacade.getInstance(myManager.getProject()).findClass(GrClosableBlock.GROOVY_LANG_CLOSURE, getResolveScope());
61   }
62
63   public String getClassName() {
64     return "Closure";
65   }
66
67   @NotNull
68   public PsiType[] getParameters() {
69     return myTypeArgs;
70   }
71
72   @NotNull
73   public ClassResolveResult resolveGenerics() {
74     final PsiClass closure = resolve();
75     final PsiSubstitutor substitutor;
76     if (closure != null && closure.getTypeParameters().length == 1) {
77       final PsiTypeParameter parameter = closure.getTypeParameters()[0];
78       final Map<PsiTypeParameter, PsiType> map = new HashMap<PsiTypeParameter, PsiType>();
79       map.put(parameter, mySignature.getReturnType());
80       substitutor = PsiSubstitutorImpl.createSubstitutor(map);
81     }
82     else {
83       substitutor = PsiSubstitutor.EMPTY;
84     }
85
86     return new ClassResolveResult() {
87       public PsiClass getElement() {
88         return closure;
89       }
90
91       public PsiSubstitutor getSubstitutor() {
92         return substitutor;
93       }
94
95       public boolean isPackagePrefixPackageReference() {
96         return false;
97       }
98
99       public boolean isAccessible() {
100         return true;
101       }
102
103       public boolean isStaticsScopeCorrect() {
104         return true;
105       }
106
107       @Nullable
108       public PsiElement getCurrentFileResolveScope() {
109         return null;
110       }
111
112       public boolean isValidResult() {
113         return isStaticsScopeCorrect() && isAccessible();
114       }
115     };
116   }
117
118   @NotNull
119   public PsiClassType rawType() {
120     return this;
121   }
122
123   @NotNull
124   public String getPresentableText() {
125     if (myTypeArgs.length == 0 || myTypeArgs[0] == null) {
126       return "Closure";
127     }
128     else {
129       return "Closure<" + myTypeArgs[0].getPresentableText() + ">";
130     }
131   }
132
133   @Nullable
134   public String getCanonicalText() {
135     if (myTypeArgs.length == 0 || myTypeArgs[0] == null) {
136       return GrClosableBlock.GROOVY_LANG_CLOSURE;
137     }
138     else {
139       return GrClosableBlock.GROOVY_LANG_CLOSURE + "<" + myTypeArgs[0].getCanonicalText() + ">";
140     }
141   }
142
143   @Nullable
144   public String getInternalCanonicalText() {
145     return getCanonicalText();
146   }
147
148   public boolean isValid() {
149     return mySignature.isValid();
150   }
151
152   public boolean equals(Object obj) {
153     if (obj instanceof GrClosureType) {
154       return Comparing.equal(mySignature, ((GrClosureType)obj).mySignature);
155     }
156
157     return super.equals(obj);
158   }
159
160   public boolean isAssignableFrom(@NotNull PsiType type) {
161     if (type instanceof GrClosureType) {
162       GrClosureType other = (GrClosureType)type;
163       GrClosureSignature otherSignature = other.mySignature;
164
165       final PsiType myReturnType = mySignature.getReturnType();
166       final PsiType otherReturnType = otherSignature.getReturnType();
167       if (myReturnType == null || otherReturnType == null) {
168         return myReturnType == null && otherReturnType == null;
169       }
170
171       if (!myReturnType.isAssignableFrom(otherReturnType)) return false;
172
173       final GrClosureParameter[] myParameters = mySignature.getParameters();
174       final GrClosureParameter[] otherParameters = otherSignature.getParameters();
175
176       if (myParameters.length != otherParameters.length) return false;
177       for (int i = 0; i < myParameters.length; i++) {
178         if (myParameters[i].isOptional() != otherParameters[i].isOptional()) return false;
179         final PsiType otherParamType = otherParameters[i].getType();
180         final PsiType myParamType = myParameters[i].getType();
181         if (myParamType == null || otherParamType == null) {
182           if (myParamType != null || otherParamType != null) return false;
183         }
184         else if (!otherParamType.isAssignableFrom(myParamType)) return false;
185       }
186       return true;
187     }
188     return super.isAssignableFrom(type);
189   }
190
191   public boolean equalsToText(@NonNls String text) {
192     return text.equals(GrClosableBlock.GROOVY_LANG_CLOSURE);
193   }
194
195   @NotNull
196   public GlobalSearchScope getResolveScope() {
197     return myScope;
198   }
199
200   @NotNull
201   public LanguageLevel getLanguageLevel() {
202     return myLanguageLevel;
203   }
204
205   public PsiClassType setLanguageLevel(final LanguageLevel languageLevel) {
206     return create(mySignature, myManager, myScope, languageLevel);
207   }
208
209   public static GrClosureType create(@NotNull GrClosableBlock closure) {
210     return create(GrClosureSignatureUtil.createSignature(closure), closure.getManager(), closure.getResolveScope(), LanguageLevel.JDK_1_5);
211   }
212
213   public static GrClosureType create(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutor) {
214     return create(GrClosureSignatureUtil.createSignature(method, substitutor), method.getManager(), GlobalSearchScope.allScope(method.getProject()), LanguageLevel.JDK_1_5);
215   }
216
217   public static GrClosureType create(@NotNull PsiParameter[] parameters,
218                                      @Nullable PsiType returnType,
219                                      PsiManager manager,
220                                      GlobalSearchScope scope,
221                                      LanguageLevel languageLevel) {
222     return create(GrClosureSignatureUtil.createSignature(parameters, returnType), manager, scope, languageLevel);
223   }
224
225   public static GrClosureType create(@NotNull GrClosureSignature signature,
226                                      PsiManager manager,
227                                      GlobalSearchScope scope,
228                                      LanguageLevel languageLevel) {
229     return new GrClosureType(languageLevel, scope, manager, signature);
230   }
231
232   @Nullable
233   public PsiType curry(int count) {
234     final GrClosureSignature newSignature = mySignature.curry(count);
235     if (newSignature == null) return null;
236     return create(newSignature, myManager, myScope, myLanguageLevel);
237   }
238
239   @NotNull
240   public GrClosureSignature getSignature() {
241     return mySignature;
242   }
243
244   public PsiType[] getClosureParameterTypes() {
245     final GrClosureParameter[] parameters = mySignature.getParameters();
246     final PsiType[] types = new PsiType[parameters.length];
247     for (int i = 0; i < types.length; i++) {
248       types[i] = parameters[i].getType();
249     }
250     return types;
251   }
252 }