Merge remote-tracking branch 'origin/master'
[idea/community.git] / plugins / groovy / groovy-psi / src / org / jetbrains / plugins / groovy / lang / psi / impl / statements / expressions / TypesUtil.java
1 /*
2  * Copyright 2000-2014 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 package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions;
17
18 import com.intellij.openapi.project.Project;
19 import com.intellij.openapi.util.Ref;
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.psi.tree.IElementType;
25 import com.intellij.psi.util.InheritanceUtil;
26 import com.intellij.psi.util.PsiUtil;
27 import com.intellij.psi.util.TypeConversionUtil;
28 import com.intellij.util.Function;
29 import com.intellij.util.containers.ComparatorUtil;
30 import com.intellij.util.containers.ContainerUtil;
31 import com.intellij.util.containers.HashMap;
32 import gnu.trove.THashMap;
33 import gnu.trove.TIntObjectHashMap;
34 import gnu.trove.TObjectIntHashMap;
35 import org.jetbrains.annotations.NonNls;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
39 import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
40 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
41 import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult;
42 import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState;
43 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation;
44 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArrayInitializer;
45 import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue;
46 import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
47 import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature;
48 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
49 import org.jetbrains.plugins.groovy.lang.psi.impl.*;
50 import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrImmediateClosureSignatureImpl;
51 import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrTypeConverter;
52 import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrTypeConverter.ApplicableTo;
53 import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
54 import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil;
55
56 import java.util.Iterator;
57 import java.util.Map;
58
59 /**
60  * @author ven
61  */
62 public class TypesUtil {
63
64   @NonNls
65   public static final Map<String, PsiType> ourQNameToUnboxed = new HashMap<String, PsiType>();
66   public static final PsiPrimitiveType[] PRIMITIVES = {
67     PsiType.BYTE,
68     PsiType.CHAR,
69     PsiType.DOUBLE,
70     PsiType.FLOAT,
71     PsiType.INT,
72     PsiType.SHORT,
73     PsiType.LONG,
74     PsiType.BOOLEAN,
75     PsiType.VOID
76   };
77
78   private TypesUtil() {
79   }
80
81   @NotNull
82   public static GroovyResolveResult[] getOverloadedOperatorCandidates(@NotNull PsiType thisType,
83                                                                       IElementType tokenType,
84                                                                       @NotNull GroovyPsiElement place,
85                                                                       PsiType[] argumentTypes) {
86     return getOverloadedOperatorCandidates(thisType, tokenType, place, argumentTypes, false);
87   }
88
89   @NotNull
90   public static GroovyResolveResult[] getOverloadedOperatorCandidates(@NotNull PsiType thisType,
91                                                                       IElementType tokenType,
92                                                                       @NotNull GroovyPsiElement place,
93                                                                       PsiType[] argumentTypes,
94                                                                       boolean incompleteCode) {
95     return ResolveUtil.getMethodCandidates(thisType, ourOperationsToOperatorNames.get(tokenType), place, true, incompleteCode, false, argumentTypes);
96   }
97
98
99   public static GroovyResolveResult[] getOverloadedUnaryOperatorCandidates(@NotNull PsiType thisType,
100                                                                            IElementType tokenType,
101                                                                            @NotNull GroovyPsiElement place,
102                                                                            PsiType[] argumentTypes) {
103     return ResolveUtil.getMethodCandidates(thisType, ourUnaryOperationsToOperatorNames.get(tokenType), place, argumentTypes);
104   }
105
106   private static final Map<IElementType, String> ourPrimitiveTypesToClassNames = new HashMap<IElementType, String>();
107   private static final String NULL = "null";
108
109   static {
110     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mSTRING_LITERAL, CommonClassNames.JAVA_LANG_STRING);
111     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mGSTRING_LITERAL, CommonClassNames.JAVA_LANG_STRING);
112     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mREGEX_LITERAL, CommonClassNames.JAVA_LANG_STRING);
113     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mDOLLAR_SLASH_REGEX_LITERAL, CommonClassNames.JAVA_LANG_STRING);
114     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_INT, CommonClassNames.JAVA_LANG_INTEGER);
115     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_LONG, CommonClassNames.JAVA_LANG_LONG);
116     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_FLOAT, CommonClassNames.JAVA_LANG_FLOAT);
117     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_DOUBLE, CommonClassNames.JAVA_LANG_DOUBLE);
118     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_BIG_INT, GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER);
119     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_BIG_DECIMAL, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL);
120     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kFALSE, CommonClassNames.JAVA_LANG_BOOLEAN);
121     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kTRUE, CommonClassNames.JAVA_LANG_BOOLEAN);
122     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kNULL, NULL);
123
124     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kINT, CommonClassNames.JAVA_LANG_INTEGER);
125     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kLONG, CommonClassNames.JAVA_LANG_LONG);
126     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kFLOAT, CommonClassNames.JAVA_LANG_FLOAT);
127     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kDOUBLE, CommonClassNames.JAVA_LANG_DOUBLE);
128     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kBOOLEAN, CommonClassNames.JAVA_LANG_BOOLEAN);
129     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kCHAR, CommonClassNames.JAVA_LANG_CHARACTER);
130     ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kBYTE, CommonClassNames.JAVA_LANG_BYTE);
131   }
132
133   private static final Map<IElementType, String> ourOperationsToOperatorNames = new HashMap<IElementType, String>();
134   private static final Map<IElementType, String> ourUnaryOperationsToOperatorNames = new HashMap<IElementType, String>();
135
136   static {
137     ourOperationsToOperatorNames.put(GroovyTokenTypes.mPLUS, "plus");
138     ourOperationsToOperatorNames.put(GroovyTokenTypes.mMINUS, "minus");
139     ourOperationsToOperatorNames.put(GroovyTokenTypes.mBAND, "and");
140     ourOperationsToOperatorNames.put(GroovyTokenTypes.mBOR, "or");
141     ourOperationsToOperatorNames.put(GroovyTokenTypes.mBXOR, "xor");
142     ourOperationsToOperatorNames.put(GroovyTokenTypes.mDIV, "div");
143     ourOperationsToOperatorNames.put(GroovyTokenTypes.mMOD, "mod");
144     ourOperationsToOperatorNames.put(GroovyTokenTypes.mSTAR, "multiply");
145     ourOperationsToOperatorNames.put(GroovyTokenTypes.kAS, "asType");
146     ourOperationsToOperatorNames.put(GroovyTokenTypes.mCOMPARE_TO, "compareTo");
147     ourOperationsToOperatorNames.put(GroovyTokenTypes.mGT, "compareTo");
148     ourOperationsToOperatorNames.put(GroovyTokenTypes.mGE, "compareTo");
149     ourOperationsToOperatorNames.put(GroovyTokenTypes.mLT, "compareTo");
150     ourOperationsToOperatorNames.put(GroovyTokenTypes.mLE, "compareTo");
151     ourOperationsToOperatorNames.put(GroovyTokenTypes.mSTAR_STAR, "power");
152     ourOperationsToOperatorNames.put(GroovyElementTypes.COMPOSITE_LSHIFT_SIGN, "leftShift");
153     ourOperationsToOperatorNames.put(GroovyElementTypes.COMPOSITE_RSHIFT_SIGN, "rightShift");
154     ourOperationsToOperatorNames.put(GroovyElementTypes.COMPOSITE_TRIPLE_SHIFT_SIGN, "rightShiftUnsigned");
155     ourOperationsToOperatorNames.put(GroovyTokenTypes.mEQUAL, "equals");
156     ourOperationsToOperatorNames.put(GroovyTokenTypes.mNOT_EQUAL, "equals");
157
158     ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mLNOT, "asBoolean");
159     ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mPLUS, "positive");
160     ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mMINUS, "negative");
161     ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mDEC, "previous");
162     ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mINC, "next");
163     ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mBNOT, "bitwiseNegate");
164   }
165
166   private static final TObjectIntHashMap<String> TYPE_TO_RANK = new TObjectIntHashMap<String>();
167
168   static {
169     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_BYTE, 1);
170     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_SHORT, 2);
171     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_INTEGER, 3);
172     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_LONG, 4);
173     TYPE_TO_RANK.put(GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER, 5);
174     TYPE_TO_RANK.put(GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL, 6);
175     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_FLOAT, 7);
176     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_DOUBLE, 8);
177     TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_NUMBER, 9);
178   }
179
180   static {
181     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_BOOLEAN, PsiType.BOOLEAN);
182     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_BYTE, PsiType.BYTE);
183     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_CHARACTER, PsiType.CHAR);
184     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_SHORT, PsiType.SHORT);
185     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_INTEGER, PsiType.INT);
186     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_LONG, PsiType.LONG);
187     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_FLOAT, PsiType.FLOAT);
188     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_DOUBLE, PsiType.DOUBLE);
189     ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_VOID, PsiType.VOID);
190   }
191
192
193   private static final TIntObjectHashMap<String> RANK_TO_TYPE = new TIntObjectHashMap<String>();
194
195   static {
196     RANK_TO_TYPE.put(1, CommonClassNames.JAVA_LANG_INTEGER);
197     RANK_TO_TYPE.put(2, CommonClassNames.JAVA_LANG_INTEGER);
198     RANK_TO_TYPE.put(3, CommonClassNames.JAVA_LANG_INTEGER);
199     RANK_TO_TYPE.put(4, CommonClassNames.JAVA_LANG_LONG);
200     RANK_TO_TYPE.put(5, GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER);
201     RANK_TO_TYPE.put(6, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL);
202     RANK_TO_TYPE.put(7, CommonClassNames.JAVA_LANG_DOUBLE);
203     RANK_TO_TYPE.put(8, CommonClassNames.JAVA_LANG_DOUBLE);
204     RANK_TO_TYPE.put(9, CommonClassNames.JAVA_LANG_NUMBER);
205   }
206
207   /**
208    * @deprecated see {@link #canAssign}
209    */
210   @Deprecated
211   public static boolean isAssignable(@Nullable PsiType lType, @Nullable PsiType rType, @NotNull PsiElement context) {
212     if (lType == null || rType == null) {
213       return false;
214     }
215     return canAssign(lType, rType, context, ApplicableTo.ASSIGNMENT) == ConversionResult.OK;
216   }
217
218   @NotNull
219   public static ConversionResult canAssign(@NotNull PsiType targetType,
220                                            @NotNull PsiType actualType,
221                                            @NotNull PsiElement context,
222                                            @NotNull ApplicableTo position) {
223     if (actualType instanceof PsiIntersectionType) {
224       ConversionResult min = ConversionResult.ERROR;
225       for (PsiType child : ((PsiIntersectionType)actualType).getConjuncts()) {
226         final ConversionResult result = canAssign(targetType, child, context, position);
227         if (result.ordinal() < min.ordinal()) {
228           min = result;
229         }
230         if (min == ConversionResult.OK) {
231           return ConversionResult.OK;
232         }
233       }
234       return min;
235     }
236     
237     if (targetType instanceof PsiIntersectionType) {
238       ConversionResult max = ConversionResult.OK;
239       for (PsiType child : ((PsiIntersectionType)targetType).getConjuncts()) {
240         final ConversionResult result = canAssign(child, actualType, context, position);
241         if (result.ordinal() > max.ordinal()) {
242           max = result;
243         }
244         if (max == ConversionResult.ERROR) {
245           return ConversionResult.ERROR;
246         }
247       }
248       return max;
249     }
250
251     final ConversionResult result = areTypesConvertible(targetType, actualType, context, position);
252     if (result != null) return result;
253
254     if (isAssignableWithoutConversions(targetType, actualType, context)) {
255       return ConversionResult.OK;
256     }
257
258     final PsiManager manager = context.getManager();
259     final GlobalSearchScope scope = context.getResolveScope();
260     targetType = boxPrimitiveType(targetType, manager, scope);
261     actualType = boxPrimitiveType(actualType, manager, scope);
262
263     if (targetType.isAssignableFrom(actualType)) {
264       return ConversionResult.OK;
265     }
266
267     return ConversionResult.ERROR;
268   }
269
270   public static boolean isAssignableByMethodCallConversion(@Nullable PsiType targetType,
271                                                            @Nullable PsiType actualType,
272                                                            @NotNull PsiElement context) {
273
274     if (targetType == null || actualType == null) return false;
275     return canAssign(targetType, actualType, context, ApplicableTo.METHOD_PARAMETER) == ConversionResult.OK;
276   }
277
278   @Nullable
279   private static ConversionResult areTypesConvertible(@NotNull PsiType targetType,
280                                                       @NotNull PsiType actualType,
281                                                       @NotNull PsiElement context,
282                                                       @NotNull ApplicableTo position) {
283     if (!(context instanceof GroovyPsiElement)) return null;
284     for (GrTypeConverter converter : GrTypeConverter.EP_NAME.getExtensions()) {
285       if (!converter.isApplicableTo(position)) continue;
286       final ConversionResult result = converter.isConvertibleEx(targetType, actualType, (GroovyPsiElement)context, position);
287       if (result != null) return result;
288     }
289     return null;
290   }
291
292   public static boolean isAssignableWithoutConversions(@Nullable PsiType lType,
293                                                        @Nullable PsiType rType,
294                                                        @NotNull PsiElement context) {
295     if (lType == null || rType == null) return false;
296
297     if (rType == PsiType.NULL) {
298       return !(lType instanceof PsiPrimitiveType);
299     }
300
301     PsiManager manager = context.getManager();
302     GlobalSearchScope scope = context.getResolveScope();
303
304     if (rType instanceof GrTupleType && ((GrTupleType)rType).getComponentTypes().length == 0) {
305       if (lType instanceof PsiArrayType ||
306           InheritanceUtil.isInheritor(lType, CommonClassNames.JAVA_UTIL_LIST) ||
307           InheritanceUtil.isInheritor(lType, CommonClassNames.JAVA_UTIL_SET)) {
308         return true;
309       }
310     }
311
312     if (rType instanceof GrTraitType) {
313       if (isAssignableWithoutConversions(lType, ((GrTraitType)rType).getExprType(), context)) return true;
314       for (PsiClassType trait : ((GrTraitType)rType).getTraitTypes()) {
315         if (isAssignableWithoutConversions(lType, trait, context)) return true;
316       }
317       return false;
318     }
319
320     if (isClassType(rType, GroovyCommonClassNames.GROOVY_LANG_GSTRING) && lType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
321       return true;
322     }
323
324     if (isNumericType(lType) && isNumericType(rType)) {
325       lType = unboxPrimitiveTypeWrapper(lType);
326       if (isClassType(lType, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL)) lType = PsiType.DOUBLE;
327       rType = unboxPrimitiveTypeWrapper(rType);
328       if (isClassType(rType, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL)) rType = PsiType.DOUBLE;
329     }
330     else {
331       rType = boxPrimitiveType(rType, manager, scope);
332       lType = boxPrimitiveType(lType, manager, scope);
333     }
334
335     if (rType instanceof GrMapType || rType instanceof GrTupleType) {
336       Boolean result = isAssignableForNativeTypes(lType, (PsiClassType)rType, context);
337       if (result != null && result.booleanValue()) return true;
338     }
339
340     if (rType instanceof GrClosureType) {
341       if (canMakeClosureRaw(lType)) {
342         rType = ((GrClosureType)rType).rawType();
343       }
344     }
345
346     return TypeConversionUtil.isAssignable(lType, rType);
347   }
348
349   private static boolean canMakeClosureRaw(PsiType type) {
350     if (!(type instanceof PsiClassType)) return true;
351
352     final PsiType[] parameters = ((PsiClassType)type).getParameters();
353
354     if (parameters.length != 1) return true;
355
356     final PsiType parameter = parameters[0];
357     if (parameter instanceof PsiWildcardType) return true;
358
359     return false;
360   }
361
362   @Nullable
363   private static Boolean isAssignableForNativeTypes(@NotNull PsiType lType,
364                                                     @NotNull PsiClassType rType,
365                                                     @NotNull PsiElement context) {
366     if (!(lType instanceof PsiClassType)) return null;
367     final PsiClassType.ClassResolveResult leftResult = ((PsiClassType)lType).resolveGenerics();
368     final PsiClassType.ClassResolveResult rightResult = rType.resolveGenerics();
369     final PsiClass leftClass = leftResult.getElement();
370     PsiClass rightClass = rightResult.getElement();
371     if (rightClass == null || leftClass == null) return null;
372
373     if (!InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true)) return Boolean.FALSE;
374
375     PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();
376
377     if (!leftClass.hasTypeParameters()) return Boolean.TRUE;
378     PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();
379
380     if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
381       rightSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor);
382       rightClass = leftClass;
383     }
384     else if (!rightClass.hasTypeParameters()) return Boolean.TRUE;
385
386     Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
387     Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
388     while (li.hasNext()) {
389       if (!ri.hasNext()) return Boolean.FALSE;
390       PsiTypeParameter lp = li.next();
391       PsiTypeParameter rp = ri.next();
392       final PsiType typeLeft = leftSubstitutor.substitute(lp);
393       if (typeLeft == null) continue;
394       final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
395       if (typeRight == null) {
396         return Boolean.TRUE;
397       }
398       if (!isAssignableWithoutConversions(typeLeft, typeRight, context)) return Boolean.FALSE;
399     }
400     return Boolean.TRUE;
401   }
402
403   @NotNull
404   public static ConversionResult canCast(@NotNull PsiType targetType, @NotNull PsiType actualType, @NotNull PsiElement context) {
405     final ConversionResult result = areTypesConvertible(targetType, actualType, context, ApplicableTo.EXPLICIT_CAST);
406     if (result != null) return result;
407     return TypeConversionUtil.areTypesConvertible(targetType, actualType) ? ConversionResult.OK : ConversionResult.ERROR;
408   }
409
410   @NotNull
411   public static ConversionResult canAssignWithinMultipleAssignment(@NotNull PsiType targetType,
412                                                                    @NotNull PsiType actualType,
413                                                                    @NotNull PsiElement context) {
414     return isAssignableWithoutConversions(targetType, actualType, context) ? ConversionResult.OK : ConversionResult.ERROR;
415   }
416
417   public static boolean isNumericType(@Nullable PsiType type) {
418     if (type instanceof PsiClassType) {
419       return TYPE_TO_RANK.contains(getQualifiedName(type));
420     }
421
422     return type instanceof PsiPrimitiveType && TypeConversionUtil.isNumericType(type);
423   }
424
425   public static PsiType unboxPrimitiveTypeWrapperAndEraseGenerics(PsiType result) {
426     return TypeConversionUtil.erasure(unboxPrimitiveTypeWrapper(result));
427   }
428
429   public static PsiType unboxPrimitiveTypeWrapper(@Nullable PsiType type) {
430     if (type instanceof PsiClassType) {
431       final PsiClass psiClass = ((PsiClassType)type).resolve();
432       if (psiClass != null) {
433         PsiType unboxed = ourQNameToUnboxed.get(psiClass.getQualifiedName());
434         if (unboxed != null) type = unboxed;
435       }
436     }
437     return type;
438   }
439
440   public static PsiType boxPrimitiveType(@Nullable PsiType result,
441                                          @NotNull PsiManager manager,
442                                          @NotNull GlobalSearchScope resolveScope,
443                                          boolean boxVoid) {
444     if (result instanceof PsiPrimitiveType && (boxVoid || result != PsiType.VOID)) {
445       PsiPrimitiveType primitive = (PsiPrimitiveType)result;
446       String boxedTypeName = primitive.getBoxedTypeName();
447       if (boxedTypeName != null) {
448         return GroovyPsiManager.getInstance(manager.getProject()).createTypeByFQClassName(boxedTypeName, resolveScope);
449       }
450     }
451
452     return result;
453   }
454
455   public static PsiType boxPrimitiveType(@Nullable PsiType result, @NotNull PsiManager manager, @NotNull GlobalSearchScope resolveScope) {
456     return boxPrimitiveType(result, manager, resolveScope, false);
457   }
458
459   @NotNull
460   public static PsiClassType createType(String fqName, @NotNull PsiElement context) {
461     return createTypeByFQClassName(fqName, context);
462   }
463
464   @NotNull
465   public static PsiClassType getJavaLangObject(@NotNull PsiElement context) {
466     return LazyFqnClassType.getLazyType(CommonClassNames.JAVA_LANG_OBJECT, context);
467   }
468
469   @Nullable
470   public static PsiType getLeastUpperBoundNullable(@Nullable PsiType type1, @Nullable PsiType type2, @NotNull PsiManager manager) {
471     if (type1 == null) return type2;
472     if (type2 == null) return type1;
473     return getLeastUpperBound(type1, type2, manager);
474   }
475
476   @Nullable
477   public static PsiType getLeastUpperBoundNullable(@NotNull Iterable<PsiType> collection, @NotNull PsiManager manager) {
478     Iterator<PsiType> iterator = collection.iterator();
479     if (!iterator.hasNext()) return null;
480     PsiType result = iterator.next();
481     while (iterator.hasNext()) {
482       result = getLeastUpperBoundNullable(result, iterator.next(), manager);
483     }
484     return result;
485   }
486
487   @Nullable
488   public static PsiType getLeastUpperBound(@NotNull PsiType type1, @NotNull PsiType type2, PsiManager manager) {
489     if (type1 instanceof GrTupleType && type2 instanceof GrTupleType) {
490       GrTupleType tuple1 = (GrTupleType)type1;
491       GrTupleType tuple2 = (GrTupleType)type2;
492       PsiType[] components1 = tuple1.getComponentTypes();
493       PsiType[] components2 = tuple2.getComponentTypes();
494
495       if (components1.length == 0) return genNewListBy(type2, manager);
496       if (components2.length == 0) return genNewListBy(type1, manager);
497
498       PsiType[] components3 = PsiType.createArray(Math.min(components1.length, components2.length));
499       for (int i = 0; i < components3.length; i++) {
500         PsiType c1 = components1[i];
501         PsiType c2 = components2[i];
502         if (c1 == null || c2 == null) {
503           components3[i] = null;
504         }
505         else {
506           components3[i] = getLeastUpperBound(c1, c2, manager);
507         }
508       }
509       return new GrImmediateTupleType(components3, JavaPsiFacade.getInstance(manager.getProject()), tuple1.getScope().intersectWith(tuple2.getResolveScope()));
510     }
511     else if (checkEmptyListAndList(type1, type2)) {
512       return genNewListBy(type2, manager);
513     }
514     else if (checkEmptyListAndList(type2, type1)) {
515       return genNewListBy(type1, manager);
516     }
517     else if (type1 instanceof GrMapType && type2 instanceof GrMapType) {
518       return GrMapType.merge(((GrMapType)type1), ((GrMapType)type2));
519     }
520     else if (checkEmptyMapAndMap(type1, type2)) {
521       return genNewMapBy(type2, manager);
522     }
523     else if (checkEmptyMapAndMap(type2, type1)) {
524       return genNewMapBy(type1, manager);
525     }
526     else if (type1 instanceof GrClosureType && type2 instanceof GrClosureType) {
527       GrClosureType clType1 = (GrClosureType)type1;
528       GrClosureType clType2 = (GrClosureType)type2;
529       GrSignature signature1 = clType1.getSignature();
530       GrSignature signature2 = clType2.getSignature();
531
532       if (signature1 instanceof GrClosureSignature && signature2 instanceof GrClosureSignature) {
533         if (((GrClosureSignature)signature1).getParameterCount() == ((GrClosureSignature)signature2).getParameterCount()) {
534           final GrClosureSignature signature = GrImmediateClosureSignatureImpl.getLeastUpperBound(((GrClosureSignature)signature1),
535                                                                                                   ((GrClosureSignature)signature2), manager);
536           if (signature != null) {
537             GlobalSearchScope scope = clType1.getResolveScope().intersectWith(clType2.getResolveScope());
538             final LanguageLevel languageLevel = ComparatorUtil.max(clType1.getLanguageLevel(), clType2.getLanguageLevel());
539             return GrClosureType.create(signature, scope, JavaPsiFacade.getInstance(manager.getProject()), languageLevel, true);
540           }
541         }
542       }
543     }
544     else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(getQualifiedName(type1)) &&
545              CommonClassNames.JAVA_LANG_STRING.equals(getQualifiedName(type2))) {
546       return type2;
547     }
548     else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(getQualifiedName(type2)) &&
549              CommonClassNames.JAVA_LANG_STRING.equals(getQualifiedName(type1))) {
550       return type1;
551     }
552     return GenericsUtil.getLeastUpperBound(type1, type2, manager);
553   }
554
555   private static boolean checkEmptyListAndList(PsiType type1, PsiType type2) {
556     if (type1 instanceof GrTupleType) {
557       PsiType[] types = ((GrTupleType)type1).getComponentTypes();
558       if (types.length == 0 && InheritanceUtil.isInheritor(type2, CommonClassNames.JAVA_UTIL_LIST)) return true;
559     }
560
561     return false;
562   }
563
564   private static PsiType genNewListBy(PsiType genericOwner, PsiManager manager) {
565     PsiClass list = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_UTIL_LIST, genericOwner.getResolveScope());
566     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
567     if (list == null) return factory.createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, null);
568     return factory.createType(list, PsiUtil.extractIterableTypeParameter(genericOwner, false));
569   }
570
571   private static boolean checkEmptyMapAndMap(PsiType type1, PsiType type2) {
572     if (type1 instanceof GrMapType) {
573       if (((GrMapType)type1).isEmpty() && InheritanceUtil.isInheritor(type2, CommonClassNames.JAVA_UTIL_MAP)) return true;
574     }
575
576     return false;
577   }
578
579   private static PsiType genNewMapBy(PsiType genericOwner, PsiManager manager) {
580     PsiClass map = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_UTIL_MAP, genericOwner.getResolveScope());
581     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
582     if (map == null) return factory.createTypeFromText(CommonClassNames.JAVA_UTIL_MAP, null);
583
584     final PsiType key = PsiUtil.substituteTypeParameter(genericOwner, CommonClassNames.JAVA_UTIL_MAP, 0, false);
585     final PsiType value = PsiUtil.substituteTypeParameter(genericOwner, CommonClassNames.JAVA_UTIL_MAP, 1, false);
586     return factory.createType(map, key, value);
587   }
588
589   @Nullable
590   public static PsiType getPsiType(PsiElement context, IElementType elemType) {
591     if (elemType == GroovyTokenTypes.kNULL) {
592       return PsiType.NULL;
593     }
594     final String typeName = getBoxedTypeName(elemType);
595     if (typeName != null) {
596       return createTypeByFQClassName(typeName, context);
597     }
598     return null;
599   }
600
601   @Nullable
602   public static String getBoxedTypeName(IElementType elemType) {
603     return ourPrimitiveTypesToClassNames.get(elemType);
604   }
605
606   @NotNull
607   public static PsiType getLeastUpperBound(PsiClass[] classes, PsiManager manager) {
608     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
609
610     if (classes.length == 0) return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT);
611
612     PsiType type = factory.createType(classes[0]);
613
614     for (int i = 1; i < classes.length; i++) {
615       PsiType t = getLeastUpperBound(type, factory.createType(classes[i]), manager);
616       if (t != null) {
617         type = t;
618       }
619     }
620
621     return type;
622   }
623
624   public static boolean isClassType(@Nullable PsiType type, @NotNull String qName) {
625     return qName.equals(getQualifiedName(type));
626   }
627
628   public static PsiSubstitutor composeSubstitutors(PsiSubstitutor s1, PsiSubstitutor s2) {
629     final Map<PsiTypeParameter, PsiType> map = s1.getSubstitutionMap();
630     Map<PsiTypeParameter, PsiType> result = new THashMap<PsiTypeParameter, PsiType>(map.size());
631     for (PsiTypeParameter parameter : map.keySet()) {
632       result.put(parameter, s2.substitute(map.get(parameter)));
633     }
634     final Map<PsiTypeParameter, PsiType> map2 = s2.getSubstitutionMap();
635     for (PsiTypeParameter parameter : map2.keySet()) {
636       if (!result.containsKey(parameter)) {
637         result.put(parameter, map2.get(parameter));
638       }
639     }
640     return PsiSubstitutorImpl.createSubstitutor(result);
641   }
642
643   @NotNull
644   public static PsiClassType createTypeByFQClassName(@NotNull String fqName, @NotNull PsiElement context) {
645     return GroovyPsiManager.getInstance(context.getProject()).createTypeByFQClassName(fqName, context.getResolveScope());
646   }
647
648   @Nullable
649   public static PsiType createJavaLangClassType(@Nullable PsiType type,
650                                                 Project project,
651                                                 GlobalSearchScope resolveScope) {
652     final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
653     PsiType result = null;
654     PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, resolveScope);
655     if (javaLangClass != null) {
656       PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
657       final PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
658       if (typeParameters.length == 1) {
659         substitutor = substitutor.put(typeParameters[0], type);
660       }
661       result = facade.getElementFactory().createType(javaLangClass, substitutor);
662     }
663     return result;
664   }
665
666   @NotNull
667   public static PsiPrimitiveType getPrimitiveTypeByText(String typeText) {
668     for (final PsiPrimitiveType primitive : PRIMITIVES) {
669       if (PsiType.VOID.equals(primitive)) {
670         return primitive;
671       }
672       if (primitive.getCanonicalText().equals(typeText)) {
673         return primitive;
674       }
675     }
676
677     assert false : "Unknown primitive type";
678     return null;
679   }
680
681   @NotNull
682   public static PsiClassType createListType(@NotNull PsiClass elements) {
683     JavaPsiFacade facade = JavaPsiFacade.getInstance(elements.getProject());
684     GlobalSearchScope resolveScope = elements.getResolveScope();
685     PsiClass listClass = facade.findClass(CommonClassNames.JAVA_UTIL_LIST, resolveScope);
686     if (listClass == null) {
687       return facade.getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_LIST, resolveScope);
688     }
689     return facade.getElementFactory().createType(listClass, facade.getElementFactory().createType(elements));
690   }
691
692   @NotNull
693   public static PsiType createSetType(@NotNull PsiElement context, @NotNull PsiType type) {
694     JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject());
695     GlobalSearchScope resolveScope = context.getResolveScope();
696
697     PsiClass setClass = facade.findClass(CommonClassNames.JAVA_UTIL_SET, resolveScope);
698     if (setClass != null && setClass.getTypeParameters().length == 1) {
699       return facade.getElementFactory().createType(setClass, type);
700     }
701
702     return facade.getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_SET, resolveScope);
703   }
704
705   public static boolean isAnnotatedCheckHierarchyWithCache(@NotNull PsiClass aClass, @NotNull String annotationFQN) {
706     Map<String, PsiClass> classMap = ClassUtil.getSuperClassesWithCache(aClass);
707
708     for (PsiClass psiClass : classMap.values()) {
709       PsiModifierList modifierList = psiClass.getModifierList();
710       if (modifierList != null) {
711         if (modifierList.findAnnotation(annotationFQN) != null) {
712           return true;
713         }
714       }
715     }
716
717     return false;
718   }
719
720   @Nullable
721   public static PsiType substituteAndNormalizeType(@Nullable PsiType type,
722                                                    @NotNull PsiSubstitutor substitutor,
723                                                    @Nullable SpreadState state, @NotNull GrExpression expression) {
724     if (type == null) return null;
725     type = substitutor.substitute(type);
726     if (type == null) return null;
727     type = PsiImplUtil.normalizeWildcardTypeByPosition(type, expression);
728     type = SpreadState.apply(type, state, expression.getProject());
729     return type;
730   }
731
732   @Nullable
733   public static PsiType getItemType(@Nullable PsiType containerType) {
734     if (containerType == null) return null;
735
736     if (containerType instanceof PsiArrayType) return ((PsiArrayType)containerType).getComponentType();
737     return PsiUtil.extractIterableTypeParameter(containerType, false);
738   }
739
740   @Nullable
741   public static PsiType inferAnnotationMemberValueType(final GrAnnotationMemberValue value) {
742     if (value instanceof GrExpression) {
743       return ((GrExpression)value).getType();
744     }
745
746     else if (value instanceof GrAnnotation) {
747       final PsiElement resolved = ((GrAnnotation)value).getClassReference().resolve();
748       if (resolved instanceof PsiClass) {
749         return JavaPsiFacade.getElementFactory(value.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY);
750       }
751
752       return null;
753     }
754
755     else if (value instanceof GrAnnotationArrayInitializer) {
756       return getTupleByAnnotationArrayInitializer((GrAnnotationArrayInitializer)value);
757     }
758
759     return null;
760   }
761
762   public static PsiType getTupleByAnnotationArrayInitializer(final GrAnnotationArrayInitializer value) {
763     return new GrTupleType(value.getResolveScope(), JavaPsiFacade.getInstance(value.getProject())) {
764       @NotNull
765       @Override
766       protected PsiType[] inferComponents() {
767         final GrAnnotationMemberValue[] initializers = value.getInitializers();
768         return ContainerUtil.map(initializers, new Function<GrAnnotationMemberValue, PsiType>() {
769           @Override
770           public PsiType fun(GrAnnotationMemberValue value) {
771             return inferAnnotationMemberValueType(value);
772           }
773         }, PsiType.createArray(initializers.length));
774       }
775
776       @Override
777       public boolean isValid() {
778         return value.isValid();
779       }
780     };
781   }
782
783   public static boolean resolvesTo(PsiType type, String fqn) {
784     if (type instanceof PsiClassType) {
785       final PsiClass resolved = ((PsiClassType)type).resolve();
786       return resolved != null && fqn.equals(resolved.getQualifiedName());
787     }
788     return false;
789   }
790
791   @Nullable
792   public static PsiType rawSecondGeneric(PsiType type, Project project) {
793     if (!(type instanceof PsiClassType)) return null;
794
795     final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics();
796     final PsiClass element = result.getElement();
797     if (element == null) return null;
798
799     final PsiType[] parameters = ((PsiClassType)type).getParameters();
800
801     boolean changed = false;
802     for (int i = 0; i < parameters.length; i++) {
803       PsiType parameter = parameters[i];
804       if (parameter == null) continue;
805
806       final Ref<PsiType> newParam = new Ref<PsiType>();
807       parameter.accept(new PsiTypeVisitorEx<Object>() {
808         @Nullable
809         @Override
810         public Object visitClassType(PsiClassType classType) {
811           if (classType.getParameterCount() > 0) {
812             newParam.set(classType.rawType());
813           }
814           return null;
815         }
816
817         @Nullable
818         @Override
819         public Object visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
820           newParam.set(capturedWildcardType.getWildcard().getBound());
821           return null;
822         }
823
824         @Nullable
825         @Override
826         public Object visitWildcardType(PsiWildcardType wildcardType) {
827           newParam.set(wildcardType.getBound());
828           return null;
829         }
830       });
831
832       if (!newParam.isNull()) {
833         changed = true;
834         parameters[i] = newParam.get();
835       }
836     }
837     if (!changed) return null;
838     return JavaPsiFacade.getElementFactory(project).createType(element, parameters);
839   }
840
841   public static boolean isPsiClassTypeToClosure(PsiType type) {
842     if (!(type instanceof PsiClassType)) return false;
843
844     final PsiClass psiClass = ((PsiClassType)type).resolve();
845     if (psiClass == null) return false;
846
847     return GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(psiClass.getQualifiedName());
848   }
849
850   @Nullable
851   public static String getQualifiedName(@Nullable PsiType type) {
852     if (type instanceof PsiClassType) {
853       PsiClass resolved = ((PsiClassType)type).resolve();
854       if (resolved instanceof PsiAnonymousClass) {
855         return getQualifiedName(((PsiAnonymousClass)resolved).getBaseClassType());
856       }
857       if (resolved != null) {
858         return resolved.getQualifiedName();
859       }
860       else {
861         return PsiNameHelper.getQualifiedClassName(type.getCanonicalText(), true);
862       }
863     }
864
865     return null;
866   }
867
868   public static boolean isEnum(PsiType type) {
869     if (type instanceof PsiClassType) {
870       final PsiClass resolved = ((PsiClassType)type).resolve();
871       return resolved != null && resolved.isEnum();
872     }
873     return false;
874   }
875 }