IDEA-133347 Can't set breakpoint and debug in decompiled code
[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       for (PsiType child : ((PsiIntersectionType)actualType).getConjuncts()) {
225         if (canAssign(targetType, child, context, position) == ConversionResult.OK) {
226           return ConversionResult.OK;
227         }
228       }
229       return ConversionResult.ERROR;
230     }
231     if (targetType instanceof PsiIntersectionType) {
232       for (PsiType child : ((PsiIntersectionType)targetType).getConjuncts()) {
233         if (canAssign(child, actualType, context, position) != ConversionResult.OK) {
234           return ConversionResult.ERROR;
235         }
236       }
237       return ConversionResult.OK;
238     }
239
240     final ConversionResult result = areTypesConvertible(targetType, actualType, context, position);
241     if (result != null) return result;
242
243     if (isAssignableWithoutConversions(targetType, actualType, context)) {
244       return ConversionResult.OK;
245     }
246
247     final PsiManager manager = context.getManager();
248     final GlobalSearchScope scope = context.getResolveScope();
249     targetType = boxPrimitiveType(targetType, manager, scope);
250     actualType = boxPrimitiveType(actualType, manager, scope);
251
252     if (targetType.isAssignableFrom(actualType)) {
253       return ConversionResult.OK;
254     }
255
256     return ConversionResult.ERROR;
257   }
258
259   public static boolean isAssignableByMethodCallConversion(@Nullable PsiType targetType,
260                                                            @Nullable PsiType actualType,
261                                                            @NotNull PsiElement context) {
262
263     if (targetType == null || actualType == null) return false;
264     return canAssign(targetType, actualType, context, ApplicableTo.METHOD_PARAMETER) == ConversionResult.OK;
265   }
266
267   @Nullable
268   private static ConversionResult areTypesConvertible(@NotNull PsiType targetType,
269                                                       @NotNull PsiType actualType,
270                                                       @NotNull PsiElement context,
271                                                       @NotNull ApplicableTo position) {
272     if (!(context instanceof GroovyPsiElement)) return null;
273     for (GrTypeConverter converter : GrTypeConverter.EP_NAME.getExtensions()) {
274       if (!converter.isApplicableTo(position)) continue;
275       final ConversionResult result = converter.isConvertibleEx(targetType, actualType, (GroovyPsiElement)context, position);
276       if (result != null) return result;
277     }
278     return null;
279   }
280
281   public static boolean isAssignableWithoutConversions(@Nullable PsiType lType,
282                                                        @Nullable PsiType rType,
283                                                        @NotNull PsiElement context) {
284     if (lType == null || rType == null) return false;
285
286     if (rType == PsiType.NULL) {
287       return !(lType instanceof PsiPrimitiveType);
288     }
289
290     PsiManager manager = context.getManager();
291     GlobalSearchScope scope = context.getResolveScope();
292
293     if (rType instanceof GrTupleType && ((GrTupleType)rType).getComponentTypes().length == 0) {
294       if (lType instanceof PsiArrayType ||
295           InheritanceUtil.isInheritor(lType, CommonClassNames.JAVA_UTIL_LIST) ||
296           InheritanceUtil.isInheritor(lType, CommonClassNames.JAVA_UTIL_SET)) {
297         return true;
298       }
299     }
300
301     if (rType instanceof GrTraitType) {
302       if (isAssignableWithoutConversions(lType, ((GrTraitType)rType).getExprType(), context)) return true;
303       for (PsiClassType trait : ((GrTraitType)rType).getTraitTypes()) {
304         if (isAssignableWithoutConversions(lType, trait, context)) return true;
305       }
306       return false;
307     }
308
309     if (isClassType(rType, GroovyCommonClassNames.GROOVY_LANG_GSTRING) && lType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
310       return true;
311     }
312
313     if (isNumericType(lType) && isNumericType(rType)) {
314       lType = unboxPrimitiveTypeWrapper(lType);
315       if (isClassType(lType, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL)) lType = PsiType.DOUBLE;
316       rType = unboxPrimitiveTypeWrapper(rType);
317       if (isClassType(rType, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL)) rType = PsiType.DOUBLE;
318     }
319     else {
320       rType = boxPrimitiveType(rType, manager, scope);
321       lType = boxPrimitiveType(lType, manager, scope);
322     }
323
324     if (rType instanceof GrMapType || rType instanceof GrTupleType) {
325       Boolean result = isAssignableForNativeTypes(lType, (PsiClassType)rType, context);
326       if (result != null && result.booleanValue()) return true;
327     }
328
329     if (rType instanceof GrClosureType) {
330       if (canMakeClosureRaw(lType)) {
331         rType = ((GrClosureType)rType).rawType();
332       }
333     }
334
335     return TypeConversionUtil.isAssignable(lType, rType);
336   }
337
338   private static boolean canMakeClosureRaw(PsiType type) {
339     if (!(type instanceof PsiClassType)) return true;
340
341     final PsiType[] parameters = ((PsiClassType)type).getParameters();
342
343     if (parameters.length != 1) return true;
344
345     final PsiType parameter = parameters[0];
346     if (parameter instanceof PsiWildcardType) return true;
347
348     return false;
349   }
350
351   @Nullable
352   private static Boolean isAssignableForNativeTypes(@NotNull PsiType lType,
353                                                     @NotNull PsiClassType rType,
354                                                     @NotNull PsiElement context) {
355     if (!(lType instanceof PsiClassType)) return null;
356     final PsiClassType.ClassResolveResult leftResult = ((PsiClassType)lType).resolveGenerics();
357     final PsiClassType.ClassResolveResult rightResult = rType.resolveGenerics();
358     final PsiClass leftClass = leftResult.getElement();
359     PsiClass rightClass = rightResult.getElement();
360     if (rightClass == null || leftClass == null) return null;
361
362     if (!InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true)) return Boolean.FALSE;
363
364     PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();
365
366     if (!leftClass.hasTypeParameters()) return Boolean.TRUE;
367     PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();
368
369     if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
370       rightSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor);
371       rightClass = leftClass;
372     }
373     else if (!rightClass.hasTypeParameters()) return Boolean.TRUE;
374
375     Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
376     Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
377     while (li.hasNext()) {
378       if (!ri.hasNext()) return Boolean.FALSE;
379       PsiTypeParameter lp = li.next();
380       PsiTypeParameter rp = ri.next();
381       final PsiType typeLeft = leftSubstitutor.substitute(lp);
382       if (typeLeft == null) continue;
383       final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
384       if (typeRight == null) {
385         return Boolean.TRUE;
386       }
387       if (!isAssignableWithoutConversions(typeLeft, typeRight, context)) return Boolean.FALSE;
388     }
389     return Boolean.TRUE;
390   }
391
392   @NotNull
393   public static ConversionResult canCast(@NotNull PsiType targetType, @NotNull PsiType actualType, @NotNull PsiElement context) {
394     final ConversionResult result = areTypesConvertible(targetType, actualType, context, ApplicableTo.EXPLICIT_CAST);
395     if (result != null) return result;
396     return TypeConversionUtil.areTypesConvertible(targetType, actualType) ? ConversionResult.OK : ConversionResult.ERROR;
397   }
398
399   @NotNull
400   public static ConversionResult canAssignWithinMultipleAssignment(@NotNull PsiType targetType,
401                                                                    @NotNull PsiType actualType,
402                                                                    @NotNull PsiElement context) {
403     return isAssignableWithoutConversions(targetType, actualType, context) ? ConversionResult.OK : ConversionResult.ERROR;
404   }
405
406   public static boolean isNumericType(@Nullable PsiType type) {
407     if (type instanceof PsiClassType) {
408       return TYPE_TO_RANK.contains(getQualifiedName(type));
409     }
410
411     return type instanceof PsiPrimitiveType && TypeConversionUtil.isNumericType(type);
412   }
413
414   public static PsiType unboxPrimitiveTypeWrapperAndEraseGenerics(PsiType result) {
415     return TypeConversionUtil.erasure(unboxPrimitiveTypeWrapper(result));
416   }
417
418   public static PsiType unboxPrimitiveTypeWrapper(@Nullable PsiType type) {
419     if (type instanceof PsiClassType) {
420       final PsiClass psiClass = ((PsiClassType)type).resolve();
421       if (psiClass != null) {
422         PsiType unboxed = ourQNameToUnboxed.get(psiClass.getQualifiedName());
423         if (unboxed != null) type = unboxed;
424       }
425     }
426     return type;
427   }
428
429   public static PsiType boxPrimitiveType(@Nullable PsiType result,
430                                          @NotNull PsiManager manager,
431                                          @NotNull GlobalSearchScope resolveScope,
432                                          boolean boxVoid) {
433     if (result instanceof PsiPrimitiveType && (boxVoid || result != PsiType.VOID)) {
434       PsiPrimitiveType primitive = (PsiPrimitiveType)result;
435       String boxedTypeName = primitive.getBoxedTypeName();
436       if (boxedTypeName != null) {
437         return GroovyPsiManager.getInstance(manager.getProject()).createTypeByFQClassName(boxedTypeName, resolveScope);
438       }
439     }
440
441     return result;
442   }
443
444   public static PsiType boxPrimitiveType(@Nullable PsiType result, @NotNull PsiManager manager, @NotNull GlobalSearchScope resolveScope) {
445     return boxPrimitiveType(result, manager, resolveScope, false);
446   }
447
448   @NotNull
449   public static PsiClassType createType(String fqName, @NotNull PsiElement context) {
450     return createTypeByFQClassName(fqName, context);
451   }
452
453   @NotNull
454   public static PsiClassType getJavaLangObject(@NotNull PsiElement context) {
455     return LazyFqnClassType.getLazyType(CommonClassNames.JAVA_LANG_OBJECT, context);
456   }
457
458   @Nullable
459   public static PsiType getLeastUpperBoundNullable(@Nullable PsiType type1, @Nullable PsiType type2, @NotNull PsiManager manager) {
460     if (type1 == null) return type2;
461     if (type2 == null) return type1;
462     return getLeastUpperBound(type1, type2, manager);
463   }
464
465   @Nullable
466   public static PsiType getLeastUpperBoundNullable(@NotNull Iterable<PsiType> collection, @NotNull PsiManager manager) {
467     Iterator<PsiType> iterator = collection.iterator();
468     if (!iterator.hasNext()) return null;
469     PsiType result = iterator.next();
470     while (iterator.hasNext()) {
471       result = getLeastUpperBoundNullable(result, iterator.next(), manager);
472     }
473     return result;
474   }
475
476   @Nullable
477   public static PsiType getLeastUpperBound(@NotNull PsiType type1, @NotNull PsiType type2, PsiManager manager) {
478     if (type1 instanceof GrTupleType && type2 instanceof GrTupleType) {
479       GrTupleType tuple1 = (GrTupleType)type1;
480       GrTupleType tuple2 = (GrTupleType)type2;
481       PsiType[] components1 = tuple1.getComponentTypes();
482       PsiType[] components2 = tuple2.getComponentTypes();
483
484       if (components1.length == 0) return genNewListBy(type2, manager);
485       if (components2.length == 0) return genNewListBy(type1, manager);
486
487       PsiType[] components3 = PsiType.createArray(Math.min(components1.length, components2.length));
488       for (int i = 0; i < components3.length; i++) {
489         PsiType c1 = components1[i];
490         PsiType c2 = components2[i];
491         if (c1 == null || c2 == null) {
492           components3[i] = null;
493         }
494         else {
495           components3[i] = getLeastUpperBound(c1, c2, manager);
496         }
497       }
498       return new GrImmediateTupleType(components3, JavaPsiFacade.getInstance(manager.getProject()), tuple1.getScope().intersectWith(tuple2.getResolveScope()));
499     }
500     else if (checkEmptyListAndList(type1, type2)) {
501       return genNewListBy(type2, manager);
502     }
503     else if (checkEmptyListAndList(type2, type1)) {
504       return genNewListBy(type1, manager);
505     }
506     else if (type1 instanceof GrMapType && type2 instanceof GrMapType) {
507       return GrMapType.merge(((GrMapType)type1), ((GrMapType)type2));
508     }
509     else if (checkEmptyMapAndMap(type1, type2)) {
510       return genNewMapBy(type2, manager);
511     }
512     else if (checkEmptyMapAndMap(type2, type1)) {
513       return genNewMapBy(type1, manager);
514     }
515     else if (type1 instanceof GrClosureType && type2 instanceof GrClosureType) {
516       GrClosureType clType1 = (GrClosureType)type1;
517       GrClosureType clType2 = (GrClosureType)type2;
518       GrSignature signature1 = clType1.getSignature();
519       GrSignature signature2 = clType2.getSignature();
520
521       if (signature1 instanceof GrClosureSignature && signature2 instanceof GrClosureSignature) {
522         if (((GrClosureSignature)signature1).getParameterCount() == ((GrClosureSignature)signature2).getParameterCount()) {
523           final GrClosureSignature signature = GrImmediateClosureSignatureImpl.getLeastUpperBound(((GrClosureSignature)signature1),
524                                                                                                   ((GrClosureSignature)signature2), manager);
525           if (signature != null) {
526             GlobalSearchScope scope = clType1.getResolveScope().intersectWith(clType2.getResolveScope());
527             final LanguageLevel languageLevel = ComparatorUtil.max(clType1.getLanguageLevel(), clType2.getLanguageLevel());
528             return GrClosureType.create(signature, scope, JavaPsiFacade.getInstance(manager.getProject()), languageLevel, true);
529           }
530         }
531       }
532     }
533     else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(getQualifiedName(type1)) &&
534              CommonClassNames.JAVA_LANG_STRING.equals(getQualifiedName(type2))) {
535       return type2;
536     }
537     else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(getQualifiedName(type2)) &&
538              CommonClassNames.JAVA_LANG_STRING.equals(getQualifiedName(type1))) {
539       return type1;
540     }
541     return GenericsUtil.getLeastUpperBound(type1, type2, manager);
542   }
543
544   private static boolean checkEmptyListAndList(PsiType type1, PsiType type2) {
545     if (type1 instanceof GrTupleType) {
546       PsiType[] types = ((GrTupleType)type1).getComponentTypes();
547       if (types.length == 0 && InheritanceUtil.isInheritor(type2, CommonClassNames.JAVA_UTIL_LIST)) return true;
548     }
549
550     return false;
551   }
552
553   private static PsiType genNewListBy(PsiType genericOwner, PsiManager manager) {
554     PsiClass list = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_UTIL_LIST, genericOwner.getResolveScope());
555     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
556     if (list == null) return factory.createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, null);
557     return factory.createType(list, PsiUtil.extractIterableTypeParameter(genericOwner, false));
558   }
559
560   private static boolean checkEmptyMapAndMap(PsiType type1, PsiType type2) {
561     if (type1 instanceof GrMapType) {
562       if (((GrMapType)type1).isEmpty() && InheritanceUtil.isInheritor(type2, CommonClassNames.JAVA_UTIL_MAP)) return true;
563     }
564
565     return false;
566   }
567
568   private static PsiType genNewMapBy(PsiType genericOwner, PsiManager manager) {
569     PsiClass map = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_UTIL_MAP, genericOwner.getResolveScope());
570     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
571     if (map == null) return factory.createTypeFromText(CommonClassNames.JAVA_UTIL_MAP, null);
572
573     final PsiType key = PsiUtil.substituteTypeParameter(genericOwner, CommonClassNames.JAVA_UTIL_MAP, 0, false);
574     final PsiType value = PsiUtil.substituteTypeParameter(genericOwner, CommonClassNames.JAVA_UTIL_MAP, 1, false);
575     return factory.createType(map, key, value);
576   }
577
578   @Nullable
579   public static PsiType getPsiType(PsiElement context, IElementType elemType) {
580     if (elemType == GroovyTokenTypes.kNULL) {
581       return PsiType.NULL;
582     }
583     final String typeName = getBoxedTypeName(elemType);
584     if (typeName != null) {
585       return createTypeByFQClassName(typeName, context);
586     }
587     return null;
588   }
589
590   @Nullable
591   public static String getBoxedTypeName(IElementType elemType) {
592     return ourPrimitiveTypesToClassNames.get(elemType);
593   }
594
595   @NotNull
596   public static PsiType getLeastUpperBound(PsiClass[] classes, PsiManager manager) {
597     PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());
598
599     if (classes.length == 0) return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT);
600
601     PsiType type = factory.createType(classes[0]);
602
603     for (int i = 1; i < classes.length; i++) {
604       PsiType t = getLeastUpperBound(type, factory.createType(classes[i]), manager);
605       if (t != null) {
606         type = t;
607       }
608     }
609
610     return type;
611   }
612
613   public static boolean isClassType(@Nullable PsiType type, @NotNull String qName) {
614     return qName.equals(getQualifiedName(type));
615   }
616
617   public static PsiSubstitutor composeSubstitutors(PsiSubstitutor s1, PsiSubstitutor s2) {
618     final Map<PsiTypeParameter, PsiType> map = s1.getSubstitutionMap();
619     Map<PsiTypeParameter, PsiType> result = new THashMap<PsiTypeParameter, PsiType>(map.size());
620     for (PsiTypeParameter parameter : map.keySet()) {
621       result.put(parameter, s2.substitute(map.get(parameter)));
622     }
623     final Map<PsiTypeParameter, PsiType> map2 = s2.getSubstitutionMap();
624     for (PsiTypeParameter parameter : map2.keySet()) {
625       if (!result.containsKey(parameter)) {
626         result.put(parameter, map2.get(parameter));
627       }
628     }
629     return PsiSubstitutorImpl.createSubstitutor(result);
630   }
631
632   @NotNull
633   public static PsiClassType createTypeByFQClassName(@NotNull String fqName, @NotNull PsiElement context) {
634     return GroovyPsiManager.getInstance(context.getProject()).createTypeByFQClassName(fqName, context.getResolveScope());
635   }
636
637   @Nullable
638   public static PsiType createJavaLangClassType(@Nullable PsiType type,
639                                                 Project project,
640                                                 GlobalSearchScope resolveScope) {
641     final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
642     PsiType result = null;
643     PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, resolveScope);
644     if (javaLangClass != null) {
645       PsiSubstitutor substitutor = PsiSubstitutor.EMPTY;
646       final PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters();
647       if (typeParameters.length == 1) {
648         substitutor = substitutor.put(typeParameters[0], type);
649       }
650       result = facade.getElementFactory().createType(javaLangClass, substitutor);
651     }
652     return result;
653   }
654
655   @NotNull
656   public static PsiPrimitiveType getPrimitiveTypeByText(String typeText) {
657     for (final PsiPrimitiveType primitive : PRIMITIVES) {
658       if (PsiType.VOID.equals(primitive)) {
659         return primitive;
660       }
661       if (primitive.getCanonicalText().equals(typeText)) {
662         return primitive;
663       }
664     }
665
666     assert false : "Unknown primitive type";
667     return null;
668   }
669
670   @NotNull
671   public static PsiClassType createListType(@NotNull PsiClass elements) {
672     JavaPsiFacade facade = JavaPsiFacade.getInstance(elements.getProject());
673     GlobalSearchScope resolveScope = elements.getResolveScope();
674     PsiClass listClass = facade.findClass(CommonClassNames.JAVA_UTIL_LIST, resolveScope);
675     if (listClass == null) {
676       return facade.getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_LIST, resolveScope);
677     }
678     return facade.getElementFactory().createType(listClass, facade.getElementFactory().createType(elements));
679   }
680
681   @NotNull
682   public static PsiType createSetType(@NotNull PsiElement context, @NotNull PsiType type) {
683     JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject());
684     GlobalSearchScope resolveScope = context.getResolveScope();
685
686     PsiClass setClass = facade.findClass(CommonClassNames.JAVA_UTIL_SET, resolveScope);
687     if (setClass != null && setClass.getTypeParameters().length == 1) {
688       return facade.getElementFactory().createType(setClass, type);
689     }
690
691     return facade.getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_SET, resolveScope);
692   }
693
694   public static boolean isAnnotatedCheckHierarchyWithCache(@NotNull PsiClass aClass, @NotNull String annotationFQN) {
695     Map<String, PsiClass> classMap = ClassUtil.getSuperClassesWithCache(aClass);
696
697     for (PsiClass psiClass : classMap.values()) {
698       PsiModifierList modifierList = psiClass.getModifierList();
699       if (modifierList != null) {
700         if (modifierList.findAnnotation(annotationFQN) != null) {
701           return true;
702         }
703       }
704     }
705
706     return false;
707   }
708
709   @Nullable
710   public static PsiType substituteAndNormalizeType(@Nullable PsiType type,
711                                                    @NotNull PsiSubstitutor substitutor,
712                                                    @Nullable SpreadState state, @NotNull GrExpression expression) {
713     if (type == null) return null;
714     type = substitutor.substitute(type);
715     if (type == null) return null;
716     type = PsiImplUtil.normalizeWildcardTypeByPosition(type, expression);
717     type = SpreadState.apply(type, state, expression.getProject());
718     return type;
719   }
720
721   @Nullable
722   public static PsiType getItemType(@Nullable PsiType containerType) {
723     if (containerType == null) return null;
724
725     if (containerType instanceof PsiArrayType) return ((PsiArrayType)containerType).getComponentType();
726     return PsiUtil.extractIterableTypeParameter(containerType, false);
727   }
728
729   @Nullable
730   public static PsiType inferAnnotationMemberValueType(final GrAnnotationMemberValue value) {
731     if (value instanceof GrExpression) {
732       return ((GrExpression)value).getType();
733     }
734
735     else if (value instanceof GrAnnotation) {
736       final PsiElement resolved = ((GrAnnotation)value).getClassReference().resolve();
737       if (resolved instanceof PsiClass) {
738         return JavaPsiFacade.getElementFactory(value.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY);
739       }
740
741       return null;
742     }
743
744     else if (value instanceof GrAnnotationArrayInitializer) {
745       return getTupleByAnnotationArrayInitializer((GrAnnotationArrayInitializer)value);
746     }
747
748     return null;
749   }
750
751   public static PsiType getTupleByAnnotationArrayInitializer(final GrAnnotationArrayInitializer value) {
752     return new GrTupleType(value.getResolveScope(), JavaPsiFacade.getInstance(value.getProject())) {
753       @NotNull
754       @Override
755       protected PsiType[] inferComponents() {
756         final GrAnnotationMemberValue[] initializers = value.getInitializers();
757         return ContainerUtil.map(initializers, new Function<GrAnnotationMemberValue, PsiType>() {
758           @Override
759           public PsiType fun(GrAnnotationMemberValue value) {
760             return inferAnnotationMemberValueType(value);
761           }
762         }, PsiType.createArray(initializers.length));
763       }
764
765       @Override
766       public boolean isValid() {
767         return value.isValid();
768       }
769     };
770   }
771
772   public static boolean resolvesTo(PsiType type, String fqn) {
773     if (type instanceof PsiClassType) {
774       final PsiClass resolved = ((PsiClassType)type).resolve();
775       return resolved != null && fqn.equals(resolved.getQualifiedName());
776     }
777     return false;
778   }
779
780   @Nullable
781   public static PsiType rawSecondGeneric(PsiType type, Project project) {
782     if (!(type instanceof PsiClassType)) return null;
783
784     final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics();
785     final PsiClass element = result.getElement();
786     if (element == null) return null;
787
788     final PsiType[] parameters = ((PsiClassType)type).getParameters();
789
790     boolean changed = false;
791     for (int i = 0; i < parameters.length; i++) {
792       PsiType parameter = parameters[i];
793       if (parameter == null) continue;
794
795       final Ref<PsiType> newParam = new Ref<PsiType>();
796       parameter.accept(new PsiTypeVisitorEx<Object>() {
797         @Nullable
798         @Override
799         public Object visitClassType(PsiClassType classType) {
800           if (classType.getParameterCount() > 0) {
801             newParam.set(classType.rawType());
802           }
803           return null;
804         }
805
806         @Nullable
807         @Override
808         public Object visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
809           newParam.set(capturedWildcardType.getWildcard().getBound());
810           return null;
811         }
812
813         @Nullable
814         @Override
815         public Object visitWildcardType(PsiWildcardType wildcardType) {
816           newParam.set(wildcardType.getBound());
817           return null;
818         }
819       });
820
821       if (!newParam.isNull()) {
822         changed = true;
823         parameters[i] = newParam.get();
824       }
825     }
826     if (!changed) return null;
827     return JavaPsiFacade.getElementFactory(project).createType(element, parameters);
828   }
829
830   public static boolean isPsiClassTypeToClosure(PsiType type) {
831     if (!(type instanceof PsiClassType)) return false;
832
833     final PsiClass psiClass = ((PsiClassType)type).resolve();
834     if (psiClass == null) return false;
835
836     return GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(psiClass.getQualifiedName());
837   }
838
839   @Nullable
840   public static String getQualifiedName(@Nullable PsiType type) {
841     if (type instanceof PsiClassType) {
842       PsiClass resolved = ((PsiClassType)type).resolve();
843       if (resolved instanceof PsiAnonymousClass) {
844         return getQualifiedName(((PsiAnonymousClass)resolved).getBaseClassType());
845       }
846       if (resolved != null) {
847         return resolved.getQualifiedName();
848       }
849       else {
850         return PsiNameHelper.getQualifiedClassName(type.getCanonicalText(), true);
851       }
852     }
853
854     return null;
855   }
856
857   public static boolean isEnum(PsiType type) {
858     if (type instanceof PsiClassType) {
859       final PsiClass resolved = ((PsiClassType)type).resolve();
860       return resolved != null && resolved.isEnum();
861     }
862     return false;
863   }
864 }