diagnostics
[idea/community.git] / java / openapi / src / com / intellij / psi / util / TypeConversionUtil.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 package com.intellij.psi.util;
17
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.roots.ProjectRootManager;
21 import com.intellij.openapi.util.Key;
22 import com.intellij.pom.java.LanguageLevel;
23 import com.intellij.psi.*;
24 import com.intellij.psi.infos.ClassCandidateInfo;
25 import com.intellij.psi.search.GlobalSearchScope;
26 import com.intellij.psi.tree.IElementType;
27 import com.intellij.util.Processor;
28 import com.intellij.util.containers.ContainerUtil;
29 import com.intellij.util.containers.HashMap;
30 import gnu.trove.THashMap;
31 import gnu.trove.THashSet;
32 import gnu.trove.TObjectIntHashMap;
33 import org.jetbrains.annotations.NotNull;
34 import org.jetbrains.annotations.Nullable;
35
36 import java.util.Collection;
37 import java.util.Iterator;
38 import java.util.Map;
39 import java.util.Set;
40
41 public class TypeConversionUtil {
42   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil");
43
44   private static final boolean[][] IS_ASSIGNABLE_BIT_SET = {
45     {true, true, false, true, true, true, true}, // byte
46     {false, true, false, true, true, true, true}, // short
47     {false, false, true, true, true, true, true}, // char
48     {false, false, false, true, true, true, true}, // int
49     {false, false, false, false, true, true, true}, // long
50     {false, false, false, false, false, true, true}, // float
51     {false, false, false, false, false, false, true}, // double
52   };
53
54   private static final TObjectIntHashMap<PsiType> TYPE_TO_RANK_MAP = new TObjectIntHashMap<PsiType>();
55
56   public static final int BYTE_RANK = 1;
57   public static final int SHORT_RANK = 2;
58   public static final int CHAR_RANK = 3;
59   public static final int INT_RANK = 4;
60   private static final int LONG_RANK = 5;
61   private static final int FLOAT_RANK = 6;
62   private static final int DOUBLE_RANK = 7;
63   private static final int BOOL_RANK = 10;
64   private static final int STRING_RANK = 100;
65   private static final int MAX_NUMERIC_RANK = DOUBLE_RANK;
66
67   static {
68     TYPE_TO_RANK_MAP.put(PsiType.BYTE, BYTE_RANK);
69     TYPE_TO_RANK_MAP.put(PsiType.SHORT, SHORT_RANK);
70     TYPE_TO_RANK_MAP.put(PsiType.CHAR, CHAR_RANK);
71     TYPE_TO_RANK_MAP.put(PsiType.INT, INT_RANK);
72     TYPE_TO_RANK_MAP.put(PsiType.LONG, LONG_RANK);
73     TYPE_TO_RANK_MAP.put(PsiType.FLOAT, FLOAT_RANK);
74     TYPE_TO_RANK_MAP.put(PsiType.DOUBLE, DOUBLE_RANK);
75     TYPE_TO_RANK_MAP.put(PsiType.BOOLEAN, BOOL_RANK);
76   }
77
78
79   /**
80    * @return true iff fromType can be casted to toType
81    */
82   public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType) {
83     if (fromType == toType) return true;
84     final boolean fromIsPrimitive = isPrimitiveAndNotNull(fromType);
85     final boolean toIsPrimitive = isPrimitiveAndNotNull(toType);
86     if (fromIsPrimitive || toIsPrimitive) {
87       if (isVoidType(fromType) || isVoidType(toType)) return false;
88       final int fromTypeRank = getTypeRank(fromType);
89       final int toTypeRank = getTypeRank(toType);
90       if (!toIsPrimitive) {
91         if (fromTypeRank == toTypeRank) return true;
92         // JLS 5.5: A value of a primitive type can be cast to a reference type by boxing conversion(see 5.1.7)
93         if (!(toType instanceof PsiClassType)) return false;
94         PsiClass toClass = ((PsiClassType)toType).resolve();
95         if (toClass == null) return false;
96         PsiClassType boxedType = ((PsiPrimitiveType)fromType).getBoxedType(toClass.getManager(), toType.getResolveScope());
97         return boxedType != null && areTypesConvertible(boxedType, toType);
98       }
99       if (!fromIsPrimitive) {
100         return fromTypeRank == toTypeRank ||
101                fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK && fromTypeRank < toTypeRank;
102       }
103       return fromTypeRank == toTypeRank ||
104              fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK;
105     }
106
107     //type can be casted via widening reference conversion
108     if (isAssignable(toType, fromType)) return true;
109
110     if (isNullType(fromType) || isNullType(toType)) return true;
111
112     // or narrowing reference conversion
113     return isNarrowingReferenceConversionAllowed(fromType, toType);
114   }
115
116   /**
117    * see JLS 5.1.5, JLS3 5.5
118    */
119   private static boolean isNarrowingReferenceConversionAllowed(PsiType fromType, PsiType toType) {
120     if (toType instanceof PsiPrimitiveType || fromType instanceof PsiPrimitiveType) return fromType.equals(toType);
121     //Done with primitives
122
123     if (toType instanceof PsiArrayType && !(fromType instanceof PsiArrayType)) {
124       if (fromType instanceof PsiClassType) {
125         final PsiClass resolved = ((PsiClassType)fromType).resolve();
126         if (resolved instanceof PsiTypeParameter) {
127           for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
128             if (!isNarrowingReferenceConversionAllowed(boundType, toType)) return false;
129           }
130           return true;
131         }
132       }
133       return isAssignable(fromType, toType);
134     }
135     if (fromType instanceof PsiArrayType) {
136       if (toType instanceof PsiClassType) {
137         final PsiClass resolved = ((PsiClassType)toType).resolve();
138         if (resolved instanceof PsiTypeParameter) {
139           for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
140             if (!isNarrowingReferenceConversionAllowed(fromType, boundType)) return false;
141           }
142           return true;
143         }
144       }
145       return toType instanceof PsiArrayType
146              && isNarrowingReferenceConversionAllowed(((PsiArrayType)fromType).getComponentType(),
147                                                       ((PsiArrayType)toType).getComponentType());
148     }
149     //Done with array types
150
151     if (fromType instanceof PsiIntersectionType) {
152       final PsiType[] conjuncts = ((PsiIntersectionType)fromType).getConjuncts();
153       for (PsiType conjunct : conjuncts) {
154         if (isNarrowingReferenceConversionAllowed(conjunct, toType)) return true;
155       }
156       return false;
157     } else if (toType instanceof PsiIntersectionType) return false;
158
159     if (fromType instanceof PsiWildcardType) {
160       final PsiWildcardType fromWildcard = (PsiWildcardType)fromType;
161       final PsiType bound = fromWildcard.getBound();
162       if (bound == null) return true;
163       if (fromWildcard.isSuper()) {
164         return isAssignable(toType, bound);
165       }
166       return isNarrowingReferenceConversionAllowed(bound, toType);
167     }
168     if (toType instanceof PsiWildcardType) {
169       final PsiWildcardType toWildcard = (PsiWildcardType)toType;
170       if (toWildcard.isSuper()) return false;
171       final PsiType bound = toWildcard.getBound();
172       return bound == null || isNarrowingReferenceConversionAllowed(fromType, bound);
173     }
174
175     if (toType instanceof PsiCapturedWildcardType) {
176       return isNarrowingReferenceConversionAllowed(fromType, ((PsiCapturedWildcardType)toType).getWildcard());
177     }
178     if (fromType instanceof PsiCapturedWildcardType) {
179       return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getWildcard(), toType);
180     }
181
182     if (isAssignable(fromType, toType)) return true;
183
184     PsiClassType fromClassType = (PsiClassType)fromType;
185     PsiClassType toClassType = (PsiClassType)toType;
186
187     PsiClassType.ClassResolveResult fromResult = fromClassType.resolveGenerics();
188     final PsiClass fromClass = fromResult.getElement();
189     if (fromClass == null) return false;
190     if (fromClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(obtainSafeSuperType((PsiTypeParameter)fromClass), toType);
191
192     PsiClassType.ClassResolveResult toResult = toClassType.resolveGenerics();
193     final PsiClass toClass = toResult.getElement();
194     if (toClass == null) return false;
195     if (toClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(fromType, obtainSafeSuperType((PsiTypeParameter)toClass));
196     //Done with type parameters
197
198     PsiManager manager = fromClass.getManager();
199     final LanguageLevel languageLevel = toClassType.getLanguageLevel();
200     if (!fromClass.isInterface()) {
201       if (toClass.isInterface()) {
202         return !fromClass.hasModifierProperty(PsiModifier.FINAL) &&
203                checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel);
204       }
205       else {
206         if (manager.areElementsEquivalent(fromClass, toClass)) {
207           return areSameParameterTypes(fromClassType, toClassType);
208         }
209
210         if (toClass.isInheritor(fromClass, true)) {
211           return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel);
212         }
213         else if (fromClass.isInheritor(toClass, true)) {
214           return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel);
215         }
216
217         return false;
218       }
219     }
220     else {
221       if (!toClass.isInterface()) {
222         if (!toClass.hasModifierProperty(PsiModifier.FINAL)) {
223           return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel);
224         }
225         else {
226           if (!toClass.isInheritor(fromClass, true)) return false;
227           PsiSubstitutor toSubstitutor = getSuperClassSubstitutor(fromClass, toClass, toResult.getSubstitutor());
228           return areSameArgumentTypes(fromClass, fromResult.getSubstitutor(), toSubstitutor);
229         }
230       }
231       else {
232         if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) {
233           //In jls2 check for method in both interfaces with the same signature but different return types.
234           Collection<HierarchicalMethodSignature> fromClassMethodSignatures = fromClass.getVisibleSignatures();
235           Collection<HierarchicalMethodSignature> toClassMethodSignatures = toClass.getVisibleSignatures();
236
237           for (HierarchicalMethodSignature fromMethodSignature : fromClassMethodSignatures) {
238             for (HierarchicalMethodSignature toMethodSignature : toClassMethodSignatures) {
239               if (fromMethodSignature.equals(toMethodSignature)) {
240                 final PsiType fromClassReturnType = fromMethodSignature.getMethod().getReturnType();
241                 final PsiType toClassReturnType = toMethodSignature.getMethod().getReturnType();
242                 if (fromClassReturnType != null
243                     && toClassReturnType != null
244                     && !fromClassReturnType.equals(toClassReturnType)) {
245                   return false;
246                 }
247               }
248             }
249           }
250           return true;
251         }
252         else {
253           //In jls3 check for super interface with distinct type arguments
254           PsiClassType.ClassResolveResult baseResult;
255           PsiClass derived;
256           PsiSubstitutor derivedSubstitutor;
257           if (toClass.isInheritor(fromClass, true)) {
258             baseResult = fromResult;
259             derived = toClass;
260             derivedSubstitutor = toResult.getSubstitutor();
261           }
262           else {
263             baseResult = toResult;
264             derived = fromClass;
265             derivedSubstitutor = fromResult.getSubstitutor();
266           }
267           return checkSuperTypesWithDifferentTypeArguments(baseResult, derived, manager, derivedSubstitutor, null, languageLevel);
268         }
269       }
270     }
271   }
272
273   private static PsiClassType obtainSafeSuperType(final PsiTypeParameter typeParameter) {
274     final PsiClassType superType = typeParameter.getSuperTypes()[0];
275     final PsiClassType.ClassResolveResult result = superType.resolveGenerics();
276     final PsiClass superClass = result.getElement();
277     if (superClass != null) {
278       final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, null);
279       return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(superClass, substitutor);
280     }
281     return superType;
282   }
283
284   private static boolean checkSuperTypesWithDifferentTypeArguments(PsiClassType.ClassResolveResult baseResult,
285                                                                    PsiClass derived,
286                                                                    PsiManager manager,
287                                                                    PsiSubstitutor derivedSubstitutor,
288                                                                    Set<PsiClass> visited,
289                                                                    final LanguageLevel languageLevel) {
290     if (visited != null && visited.contains(derived)) return true;
291
292     if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) return true;
293     PsiClass base = baseResult.getElement();
294     PsiClass[] supers = derived.getSupers();
295     if (manager.areElementsEquivalent(base, derived)) {
296       derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
297       return areSameArgumentTypes(derived, baseResult.getSubstitutor(), derivedSubstitutor);
298     }
299     else if (base.isInheritor(derived, true)) {
300       derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
301       PsiSubstitutor baseSubstitutor = getSuperClassSubstitutor(derived, base, baseResult.getSubstitutor());
302       if (!areSameArgumentTypes(derived, baseSubstitutor, derivedSubstitutor)) return false;
303     }
304
305     if (visited == null) visited = new THashSet<PsiClass>();
306     visited.add(derived);
307     for (PsiClass aSuper : supers) {
308       PsiSubstitutor s = getSuperClassSubstitutor(aSuper, derived, derivedSubstitutor);
309       if (!checkSuperTypesWithDifferentTypeArguments(baseResult, aSuper, manager, s, visited, languageLevel)) return false;
310     }
311
312     return true;
313   }
314
315   private static boolean areSameParameterTypes(PsiClassType type1, PsiClassType type2) {
316     PsiClassType.ClassResolveResult resolveResult1 = type1.resolveGenerics();
317     PsiClassType.ClassResolveResult resolveResult2 = type2.resolveGenerics();
318     final PsiClass aClass = resolveResult1.getElement();
319     final PsiClass bClass = resolveResult2.getElement();
320     return aClass != null &&
321            bClass != null &&
322            aClass.getManager().areElementsEquivalent(aClass, bClass) &&
323            areSameArgumentTypes(aClass, resolveResult1.getSubstitutor(), resolveResult2.getSubstitutor());
324   }
325
326   private static boolean areSameArgumentTypes(PsiClass aClass, PsiSubstitutor substitutor1, PsiSubstitutor substitutor2) {
327     for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) {
328       PsiType typeArg1 = substitutor1.substitute(typeParameter);
329       PsiType typeArg2 = substitutor2.substitute(typeParameter);
330       if (typeArg1 == null || typeArg2 == null) return true;
331       if (typeArg1 instanceof PsiWildcardType || typeArg2 instanceof PsiWildcardType) return true;
332       if (typeArg1 instanceof PsiCapturedWildcardType || typeArg2 instanceof PsiCapturedWildcardType) return true;
333
334       if (typeArg1 instanceof PsiClassType && ((PsiClassType)typeArg1).resolve() instanceof PsiTypeParameter) return true;
335       if (typeArg2 instanceof PsiClassType && ((PsiClassType)typeArg2).resolve() instanceof PsiTypeParameter) return true;
336       if (!typeArg1.equals(typeArg2)) return false;
337     }
338
339     return true;
340   }
341
342   public static boolean isPrimitiveAndNotNull(PsiType type) {
343     return type instanceof PsiPrimitiveType && !isNullType(type);
344   }
345
346   public static boolean isEnumType(PsiType type) {
347     if (type instanceof PsiClassType) {
348       final PsiClass psiClass = ((PsiClassType)type).resolve();
349       return psiClass != null && psiClass.isEnum();
350     }
351     return false;
352   }
353
354   public static boolean isNullType(PsiType type) {
355     return PsiType.NULL.equals(type);
356   }
357
358   public static boolean isDoubleType(PsiType type) {
359     return PsiType.DOUBLE.equals(type) || PsiType.DOUBLE.equals(PsiPrimitiveType.getUnboxedType(type));
360   }
361
362   public static boolean isFloatType(PsiType type) {
363     return PsiType.FLOAT.equals(type) || PsiType.FLOAT.equals(PsiPrimitiveType.getUnboxedType(type));
364   }
365
366   public static boolean isLongType(PsiType type) {
367     return PsiType.LONG.equals(type) || PsiType.LONG.equals(PsiPrimitiveType.getUnboxedType(type));
368   }
369
370   public static boolean isVoidType(PsiType type) {
371     return PsiType.VOID.equals(type);
372   }
373
374   public static boolean isBooleanType(PsiType type) {
375     return PsiType.BOOLEAN.equals(type) || PsiType.BOOLEAN.equals(PsiPrimitiveType.getUnboxedType(type));
376   }
377
378   public static boolean isNumericType(int typeRank) {
379     return typeRank <= MAX_NUMERIC_RANK;
380   }
381   public static boolean isNumericType(PsiType type) {
382     return type != null && isNumericType(getTypeRank(type));
383   }
384
385   /**
386    * @return 1..MAX_NUMERIC_TYPE if type is primitive numeric type,
387    *         BOOL_TYPE for boolean,
388    *         STRING_TYPE for String,
389    *         Integer.MAX_VALUE for others
390    */
391   public static int getTypeRank(PsiType type) {
392     PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
393     if (unboxedType != null) {
394       type = unboxedType;
395     }
396
397     int rank = TYPE_TO_RANK_MAP.get(type);
398     if (rank != 0) return rank;
399     if (type.equalsToText("java.lang.String")) return STRING_RANK;
400     return Integer.MAX_VALUE;
401   }
402
403   /**
404    * @param tokenType JavaTokenType enumeration
405    * @param lOperand
406    * @param rOperand
407    * @param strict    true if operator result type should be convertible to the left operand
408    * @return true if lOperand operator rOperand expression is syntactically correct
409    */
410   public static boolean isBinaryOperatorApplicable(IElementType tokenType,
411                                                    PsiExpression lOperand,
412                                                    PsiExpression rOperand,
413                                                    boolean strict) {
414     if (lOperand == null || rOperand == null) return true;
415     final PsiType ltype = lOperand.getType();
416     final PsiType rtype = rOperand.getType();
417     return isBinaryOperatorApplicable(tokenType, ltype, rtype, strict);
418   }
419
420   public static boolean isBinaryOperatorApplicable(final IElementType tokenType, final PsiType ltype, final PsiType rtype, final boolean strict) {
421     if (ltype == null || rtype == null) return true;
422     int resultTypeRank = BOOL_RANK;
423     boolean isApplicable = false;
424     final int ltypeRank = getTypeRank(ltype);
425     final int rtypeRank = getTypeRank(rtype);
426     Label:
427     if (tokenType == JavaTokenType.LT || tokenType == JavaTokenType.LE || tokenType == JavaTokenType.GT || tokenType == JavaTokenType.GE) {
428       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
429         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
430       }
431     }
432     else if (tokenType == JavaTokenType.EQEQ || tokenType == JavaTokenType.NE) {
433       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype) &&
434           (isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype))) {
435         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK
436                        || ltypeRank == BOOL_RANK && rtypeRank == BOOL_RANK;
437       }
438       else {
439         if (isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype)) return false;
440         isApplicable = areTypesConvertible(ltype, rtype) || areTypesConvertible(rtype, ltype);
441       }
442     }
443     else if (tokenType == JavaTokenType.PLUS) {
444       if (ltype.equalsToText("java.lang.String")) {
445         isApplicable = !isVoidType(rtype);
446         resultTypeRank = STRING_RANK;
447         break Label;
448       }
449       else if (rtype.equalsToText("java.lang.String")) {
450         isApplicable = !isVoidType(ltype);
451         resultTypeRank = STRING_RANK;
452         break Label;
453       }
454       //fallthrough
455
456
457
458
459       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
460         resultTypeRank = Math.max(ltypeRank, rtypeRank);
461         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
462       }
463     }
464     else if (tokenType == JavaTokenType.ASTERISK || tokenType == JavaTokenType.DIV || tokenType == JavaTokenType.PERC ||
465              tokenType == JavaTokenType.MINUS) {
466       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
467         resultTypeRank = Math.max(ltypeRank, rtypeRank);
468         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
469       }
470     }
471     else if (tokenType == JavaTokenType.LTLT || tokenType == JavaTokenType.GTGT || tokenType == JavaTokenType.GTGTGT) {
472       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
473         isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK;
474         resultTypeRank = INT_RANK;
475       }
476     }
477     else if (tokenType == JavaTokenType.AND || tokenType == JavaTokenType.OR || tokenType == JavaTokenType.XOR) {
478       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
479         isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK
480                        || isBooleanType(ltype) && isBooleanType(rtype);
481         resultTypeRank = ltypeRank <= LONG_RANK ? INT_RANK : BOOL_RANK;
482       }
483     }
484     else if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) {
485       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
486         isApplicable = isBooleanType(ltype) && isBooleanType(rtype);
487       }
488     }
489     if (isApplicable && strict) {
490       if (resultTypeRank > MAX_NUMERIC_RANK) {
491         isApplicable = ltypeRank == resultTypeRank || ltype.equalsToText("java.lang.Object");
492       }
493       else {
494         isApplicable = ltypeRank <= MAX_NUMERIC_RANK;
495       }
496     }
497     return isApplicable;
498   }
499
500   public static boolean isPrimitiveAndNotNullOrWrapper(PsiType type) {
501     if (type instanceof PsiClassType) {
502       return PsiPrimitiveType.getUnboxedType(type) != null;
503     }
504
505     return isPrimitiveAndNotNull(type);
506   }
507
508   public static boolean isUnaryOperatorApplicable(PsiJavaToken token, PsiExpression operand) {
509     if (operand == null) return false;
510     PsiType type = operand.getType();
511     return type != null && isUnaryOperatorApplicable(token, type);
512   }
513
514   public static boolean isUnaryOperatorApplicable(final PsiJavaToken token, final PsiType type) {
515     IElementType i = token.getTokenType();
516     int typeRank = getTypeRank(type);
517     if (i == JavaTokenType.MINUSMINUS || i == JavaTokenType.PLUSPLUS) {
518       return typeRank <= MAX_NUMERIC_RANK;
519     }
520     else if (i == JavaTokenType.MINUS || i == JavaTokenType.PLUS) {
521       return typeRank <= MAX_NUMERIC_RANK;
522     }
523     else if (i == JavaTokenType.TILDE) {
524       return typeRank <= LONG_RANK;
525     }
526     else if (i == JavaTokenType.EXCL) {
527       return typeRank == BOOL_RANK;
528     }
529     else {
530       LOG.error("unknown token: " + token);
531     }
532     return true;
533   }
534
535   /**
536    * @return true if expression can be the left part of assignment operator
537    */
538   public static boolean isLValue(PsiExpression element) {
539     if (element instanceof PsiReferenceExpression) {
540       final PsiReferenceExpression expression = (PsiReferenceExpression)element;
541       final PsiElement resolved = expression.resolve();
542       return resolved instanceof PsiVariable;
543     }
544     if (element instanceof PsiParenthesizedExpression) {
545       return isLValue(((PsiParenthesizedExpression)element).getExpression());
546     }
547     if (element instanceof PsiArrayAccessExpression) {
548       final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)element;
549       final PsiExpression arrayExpression = arrayAccessExpression.getArrayExpression();
550       final PsiType type = arrayExpression.getType();
551       if (type == null || !(type instanceof PsiArrayType)) return false;
552       final PsiExpression indexExpression = arrayAccessExpression.getIndexExpression();
553       if (indexExpression == null) return false;
554       final PsiType indexType = indexExpression.getType();
555       if (indexType == null) return false;
556       if (getTypeRank(indexType) <= INT_RANK) return true;
557     }
558     return false;
559   }
560
561
562   /**
563    * JLS 5.2
564    */
565   public static boolean areTypesAssignmentCompatible(PsiType lType, PsiExpression rExpr) {
566     if (lType == null || rExpr == null) return true;
567     PsiType rType = rExpr.getType();
568     if (rType == null) return false;
569     if (isAssignable(lType, rType)) return true;
570     if (lType instanceof PsiClassType) {
571         lType = PsiPrimitiveType.getUnboxedType(lType);
572         if (lType == null) return false;
573     }
574
575     final int rTypeRank = getTypeRank(rType);
576     if (lType instanceof PsiPrimitiveType
577         && rType instanceof PsiPrimitiveType
578         && rTypeRank >= BYTE_RANK && rTypeRank <= INT_RANK) {
579       final Object rValue = JavaPsiFacade.getInstance(rExpr.getProject()).getConstantEvaluationHelper().computeConstantExpression(rExpr);
580       final long value;
581       if (rValue instanceof Number) {
582         value = ((Number)rValue).longValue();
583       }
584       else if (rValue instanceof Character) {
585         value = ((Character)rValue).charValue();
586       }
587       else {
588         return false;
589       }
590
591       if (PsiType.BYTE.equals(lType)) {
592         return -128 <= value && value <= 127;
593       }
594       else if (PsiType.SHORT.equals(lType)) {
595         return -32768 <= value && value <= 32767;
596       }
597       else if (PsiType.CHAR.equals(lType)) {
598         return 0 <= value && value <= 0xFFFF;
599       }
600     }
601     return false;
602   }
603
604   /**
605    * Checks whether values of one type can be assigned to another
606    *
607    * @param left  type to assign to
608    * @param right type of value
609    * @return true if value of type <code>right</code> can be assigned to an l-value of
610    *         type <code>left</code>
611    */
612   public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right) {
613     return isAssignable(left, right, true);
614   }
615
616   public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right, boolean allowUncheckedConversion) {
617     if (left == right || left.equals(right)) return true;
618     if (isNullType(right)) {
619       return !(left instanceof PsiPrimitiveType) || isNullType(left);
620     }
621
622     if (left instanceof PsiIntersectionType) {
623       PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts();
624       for (PsiType conjunct : conjuncts) {
625         if (!isAssignable(conjunct, right, allowUncheckedConversion)) return false;
626       }
627       return true;
628     }
629     if (right instanceof PsiIntersectionType) {
630       PsiType[] conjuncts = ((PsiIntersectionType)right).getConjuncts();
631       for (PsiType conjunct : conjuncts) {
632         if (isAssignable(left, conjunct, allowUncheckedConversion)) return true;
633       }
634       return false;
635     }
636
637     if (left instanceof PsiCapturedWildcardType) {
638       return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion);
639     }
640     if (right instanceof PsiCapturedWildcardType) {
641       return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion);
642     }
643
644     if (left instanceof PsiWildcardType) {
645       return isAssignableToWildcard((PsiWildcardType)left, right);
646     }
647     if (right instanceof PsiWildcardType) {
648       return isAssignableFromWildcard(left, (PsiWildcardType)right);
649     }
650     if (right instanceof PsiArrayType) {
651       if (!(left instanceof PsiArrayType)) {
652         if (left instanceof PsiPrimitiveType || PsiUtil.resolveClassInType(left) == null) return false;
653         PsiClass lClass = PsiUtil.resolveClassInType(left);
654         if (lClass == null) return false;
655         if (lClass.isInterface()) {
656           final String qualifiedName = lClass.getQualifiedName();
657           return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName);
658         }
659         else {
660           return left.equalsToText("java.lang.Object");
661         }
662       }
663       PsiType lCompType = ((PsiArrayType)left).getComponentType();
664       PsiType rCompType = ((PsiArrayType)right).getComponentType();
665       if (lCompType instanceof PsiPrimitiveType) {
666         return lCompType.equals(rCompType);
667       }
668       else {
669         return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion);
670       }
671     }
672
673     if (left instanceof PsiArrayType) return false;
674     if (right instanceof PsiPrimitiveType) {
675       if (isVoidType(right)) return false;
676       if (!(left instanceof PsiPrimitiveType)) {
677         return left instanceof PsiClassType && isBoxable((PsiClassType)left, (PsiPrimitiveType)right);
678       }
679       int leftTypeIndex = TYPE_TO_RANK_MAP.get(left) - 1;
680       int rightTypeIndex = TYPE_TO_RANK_MAP.get(right) - 1;
681       return leftTypeIndex >= 0 &&
682              rightTypeIndex >= 0 &&
683              rightTypeIndex < IS_ASSIGNABLE_BIT_SET.length &&
684              leftTypeIndex < IS_ASSIGNABLE_BIT_SET.length &&
685              IS_ASSIGNABLE_BIT_SET[rightTypeIndex][leftTypeIndex];
686     }
687     if (!(right instanceof PsiClassType)) {
688       LOG.error(right);
689     }
690     if (left instanceof PsiPrimitiveType) {
691       return isUnboxable((PsiPrimitiveType)left, (PsiClassType)right);
692     }
693     final PsiClassType.ClassResolveResult leftResult = PsiUtil.resolveGenericsClassInType(left);
694     final PsiClassType.ClassResolveResult rightResult = PsiUtil.resolveGenericsClassInType(right);
695     if (leftResult.getElement() == null || rightResult.getElement() == null) {
696       if (leftResult.getElement() != rightResult.getElement()) return false;
697       // let's suppose 2 unknown classes, which could be the same to be the same
698       String lText = left.getPresentableText();
699       String rText = right.getPresentableText();
700       if (lText.equals(rText)) return true;
701       if (lText.length() > rText.length() && lText.endsWith(rText) &&
702           lText.charAt(lText.length() - rText.length() - 1) == '.') {
703         return true;
704       }
705       return rText.length() > lText.length()
706              && rText.endsWith(lText)
707              && rText.charAt(rText.length() - lText.length() - 1) == '.';
708     }
709     return isClassAssignable(leftResult, rightResult, allowUncheckedConversion);
710   }
711
712   private static boolean isAssignableFromWildcard(PsiType left, PsiWildcardType rightWildcardType) {
713     return isAssignable(left, rightWildcardType.getExtendsBound());
714   }
715
716   private static boolean isAssignableToWildcard(PsiWildcardType wildcardType, PsiType right) {
717     if (wildcardType.isSuper()) {
718       return isAssignable(right, wildcardType.getSuperBound());
719     }
720     return isAssignable(wildcardType.getExtendsBound(), right);
721   }
722
723   private static boolean isUnboxable(final PsiPrimitiveType left, final PsiClassType right) {
724     final PsiPrimitiveType rightUnboxedType = PsiPrimitiveType.getUnboxedType(right);
725     return rightUnboxedType != null && isAssignable(left, rightUnboxedType);
726   }
727
728   public static boolean boxingConversionApplicable(final PsiType left, final PsiType right) {
729     if (left instanceof PsiPrimitiveType && !PsiType.NULL.equals(left)) {
730       return right instanceof PsiClassType && isAssignable(left, right);
731     }
732     else {
733       return left instanceof PsiClassType
734                 && right instanceof PsiPrimitiveType
735                 && !PsiType.NULL.equals(right)
736                 && isAssignable(left, right);
737     }
738   }
739
740   private static final Key<CachedValue<Set<String>>> POSSIBLE_BOXED_HOLDER_TYPES = Key.create("Types that may be possibly assigned from primitive ones");
741
742   private static boolean isBoxable(final PsiClassType left, final PsiPrimitiveType right) {
743     if (!left.getLanguageLevel().hasEnumKeywordAndAutoboxing()) return false;
744     final PsiClass psiClass = left.resolve();
745     if (psiClass == null) return false;
746
747     final String qname = psiClass.getQualifiedName();
748     if (qname == null || !getAllBoxedTypeSupers(psiClass).contains(qname)) {
749       return false;
750     }
751
752     final PsiClassType rightBoxed = right.getBoxedType(psiClass.getManager(), left.getResolveScope());
753     return rightBoxed != null && isAssignable(left, rightBoxed);
754   }
755
756   private static Set<String> getAllBoxedTypeSupers(PsiClass psiClass) {
757     PsiManager manager = psiClass.getManager();
758     final Project project = psiClass.getProject();
759     CachedValue<Set<String>> boxedHolderTypes = project.getUserData(POSSIBLE_BOXED_HOLDER_TYPES);
760     if (boxedHolderTypes == null) {
761       project.putUserData(POSSIBLE_BOXED_HOLDER_TYPES, boxedHolderTypes = CachedValuesManager.getManager(manager.getProject()).createCachedValue(new CachedValueProvider<Set<String>>() {
762         public Result<Set<String>> compute() {
763           final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
764           final Set<String> set = new THashSet<String>();
765           for (final String qname : PsiPrimitiveType.getAllBoxedTypeNames()) {
766             final PsiClass boxedClass = facade.findClass(qname, GlobalSearchScope.allScope(project));
767             InheritanceUtil.processSupers(boxedClass, true, new Processor<PsiClass>() {
768               public boolean process(PsiClass psiClass) {
769                 ContainerUtil.addIfNotNull(psiClass.getQualifiedName(), set);
770                 return true;
771               }
772             });
773           }
774           return Result.create(set, ProjectRootManager.getInstance(project));
775         }
776       }, false));
777     }
778
779     final Set<String> boxedHolders = boxedHolderTypes.getValue();
780     return boxedHolders;
781   }
782
783   private static boolean isClassAssignable(PsiClassType.ClassResolveResult leftResult,
784                                            PsiClassType.ClassResolveResult rightResult,
785                                            boolean allowUncheckedConversion) {
786     final PsiClass leftClass = leftResult.getElement();
787     final PsiClass rightClass = rightResult.getElement();
788     return leftClass != null
789            && rightClass != null
790            && InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true)
791            && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion);
792   }
793
794   private static boolean typeParametersAgree(PsiClassType.ClassResolveResult leftResult,
795                                              PsiClassType.ClassResolveResult rightResult,
796                                              boolean allowUncheckedConversion) {
797     PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();
798     PsiClass leftClass = leftResult.getElement();
799     PsiClass rightClass = rightResult.getElement();
800
801     if (!leftClass.hasTypeParameters()) return true;
802     PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();
803
804     if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
805       rightSubstitutor = getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor);
806       rightClass = leftClass;
807     }
808     else if (!rightClass.hasTypeParameters()) return true;
809
810     Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
811     Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
812     while (li.hasNext()) {
813       if (!ri.hasNext()) return false;
814       PsiTypeParameter lp = li.next();
815       PsiTypeParameter rp = ri.next();
816       final PsiType typeLeft = leftSubstitutor.substitute(lp);
817       if (typeLeft == null) continue;
818       final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
819       if (typeRight == null) {
820         // compatibility feature: allow to assign raw types to generic ones
821         return allowUncheckedConversion;
822       }
823       if (!typesAgree(typeLeft, typeRight)) return false;
824     }
825     return true;
826   }
827
828   private static boolean typesAgree(PsiType typeLeft, PsiType typeRight) {
829     if (typeLeft instanceof PsiWildcardType) {
830       final PsiWildcardType leftWildcard = (PsiWildcardType)typeLeft;
831       final PsiType leftBound = leftWildcard.getBound();
832       if (leftBound == null) return true;
833       if (leftBound.equalsToText("java.lang.Object")) {
834         if (!leftWildcard.isSuper()) return true;
835         if (typeRight.equalsToText("java.lang.Object")) return true;
836       }
837
838       if (typeRight instanceof PsiWildcardType) {
839         final PsiWildcardType rightWildcard = (PsiWildcardType)typeRight;
840         if (leftWildcard.isExtends()) {
841           return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), false);
842         }
843         else { //isSuper
844           return rightWildcard.isSuper() && isAssignable(rightWildcard.getBound(), leftBound, false);
845         }
846       }
847       else {
848         if (leftWildcard.isExtends()) {
849           return isAssignable(leftBound, typeRight, false);
850         }
851         else { // isSuper
852           return isAssignable(typeRight, leftBound, false);
853         }
854       }
855     }
856     else {
857       return typeLeft.equals(typeRight);
858     }
859   }
860
861   @Nullable
862   public static PsiSubstitutor getClassSubstitutor(@NotNull PsiClass superClassCandidate,
863                                                    @NotNull PsiClass derivedClassCandidate,
864                                                    PsiSubstitutor derivedSubstitutor) {
865     if (superClassCandidate.getManager().areElementsEquivalent(superClassCandidate, derivedClassCandidate)) {
866       PsiTypeParameter[] baseParams = superClassCandidate.getTypeParameters();
867       PsiTypeParameter[] derivedParams = derivedClassCandidate.getTypeParameters();
868       if (baseParams.length > 0 && derivedParams.length == 0) {
869         return JavaPsiFacade.getInstance(superClassCandidate.getProject()).getElementFactory().createRawSubstitutor(superClassCandidate);
870       }
871       return derivedSubstitutor;
872     }
873     if (!derivedClassCandidate.isInheritor(superClassCandidate, true)) return null;
874     return getSuperClassSubstitutor(superClassCandidate, derivedClassCandidate, derivedSubstitutor);
875   }
876
877   /**
878    * Calculates substitutor that binds type parameters in <code>superClass</code> with
879    * values that they have in <code>derivedClass</code>, given that type parameters in
880    * <code>derivedClass</code> are bound by <code>derivedSubstitutor</code>.
881    * <code>superClass</code> must be a super class/interface of <code>derivedClass</code> (as in
882    * <code>InheritanceUtil.isInheritor(derivedClass, superClass, true)</code>
883    *
884    * @param superClass
885    * @param derivedClass
886    * @param derivedSubstitutor
887    * @return substitutor (never returns <code>null</code>)
888    * @see InheritanceUtil#isInheritor(PsiClass, PsiClass, boolean)
889    */
890   @NotNull
891   public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass,
892                                                         PsiClass derivedClass,
893                                                         PsiSubstitutor derivedSubstitutor) {
894     // [dsl] assertion commented out since we no longer cache isInheritor
895     //LOG.assertTrue(derivedClass.isInheritor(superClass, true), "Not inheritor: " + derivedClass + " super: " + superClass);
896
897     if (!superClass.hasTypeParameters() && superClass.getContainingClass() == null) return PsiSubstitutor.EMPTY; //optimization
898
899     final PsiManager manager = superClass.getManager();
900     if (PsiUtil.isRawSubstitutor(derivedClass, derivedSubstitutor)) {
901       return JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createRawSubstitutor(superClass);
902     }
903
904     final PsiClass objectClass = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Object", superClass.getResolveScope());
905     if (manager.areElementsEquivalent(superClass, objectClass)) {
906       return PsiSubstitutor.EMPTY;
907     }
908
909     PsiSubstitutor substitutor;
910     final Set<PsiClass> visited = new THashSet<PsiClass>();
911     if (derivedClass instanceof PsiAnonymousClass) {
912       final PsiClassType baseType = ((PsiAnonymousClass)derivedClass).getBaseClassType();
913       final JavaResolveResult result = baseType.resolveGenerics();
914       if (result.getElement() == null) return PsiSubstitutor.UNKNOWN;
915       substitutor = getSuperClassSubstitutorInner(superClass, (PsiClass)result.getElement(),
916                                                   derivedSubstitutor.putAll(result.getSubstitutor()), visited, manager);
917     }
918     else {
919       substitutor = getSuperClassSubstitutorInner(superClass, derivedClass, derivedSubstitutor, visited, manager);
920     }
921     if (substitutor == null) {
922       LOG.error(
923         "Not inheritor: " + derivedClass + "(" + derivedClass.getClass().getName() + "; " + PsiUtil.getVirtualFile(derivedClass) + ");" +
924         "\n super: " + superClass + "(" + superClass.getClass().getName() + "; " + PsiUtil.getVirtualFile(superClass) + ")");
925     }
926     return substitutor;
927   }
928
929   @NotNull
930   public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, @NotNull PsiClassType classType) {
931       final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
932       return getSuperClassSubstitutor(superClass, classResolveResult.getElement(), classResolveResult.getSubstitutor());
933   }
934
935   private static PsiSubstitutor getSuperClassSubstitutorInner(PsiClass base,
936                                                               PsiClass candidate,
937                                                               PsiSubstitutor candidateSubstitutor,
938                                                               Set<PsiClass> visited,
939                                                               PsiManager manager) {
940     if (!visited.add(candidate)) return null;
941
942     if (base == candidate) return candidateSubstitutor;
943     if (manager.areElementsEquivalent(base, candidate)) {
944       PsiTypeParameter[] baseParams = base.getTypeParameters();
945       PsiTypeParameter[] candidateParams = candidate.getTypeParameters();
946       PsiElementFactory factory = JavaPsiFacade.getInstance(base.getProject()).getElementFactory();
947       if (baseParams.length > 0 && candidateParams.length == 0) {
948         return factory.createRawSubstitutor(base);
949       }
950       else {
951         Map<PsiTypeParameter, PsiType> m = new HashMap<PsiTypeParameter, PsiType>();
952         for (int i = 0; i < candidateParams.length && i < baseParams.length; i++) {
953           m.put(baseParams[i], candidateSubstitutor.substitute(candidateParams[i]));
954         }
955         return factory.createSubstitutor(m);
956       }
957     }
958
959     PsiSubstitutor substitutor = checkReferenceList(candidate.getExtendsListTypes(), candidateSubstitutor, base, visited,
960                                                     manager);
961     if (substitutor == null) {
962       substitutor = checkReferenceList(candidate.getImplementsListTypes(), candidateSubstitutor, base, visited, manager);
963     }
964     return substitutor;
965   }
966
967   private static PsiSubstitutor checkReferenceList(final PsiClassType[] types, PsiSubstitutor candidateSubstitutor,
968                                                    PsiClass base,
969                                                    Set<PsiClass> set,
970                                                    PsiManager manager) {
971     for (final PsiClassType type : types) {
972       final PsiType substitutedType = candidateSubstitutor.substitute(type);
973       //if (!(substitutedType instanceof PsiClassType)) return null;
974       LOG.assertTrue(substitutedType instanceof PsiClassType);
975
976       final JavaResolveResult result = ((PsiClassType)substitutedType).resolveGenerics();
977       final PsiElement newCandidate = result.getElement();
978       if (newCandidate != null) {
979         final PsiSubstitutor substitutor = result.getSubstitutor();
980         final PsiSubstitutor newSubstitutor = getSuperClassSubstitutorInner(base, (PsiClass)newCandidate,
981                                                                             substitutor, set, manager);
982         if (newSubstitutor != null) {
983           return type.isRaw() ? JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createRawSubstitutor(base) : newSubstitutor;
984         }
985       }
986     }
987     return null;
988   }
989
990   /**
991    * see JLS 5.6.2
992    */
993   public static PsiType binaryNumericPromotion(PsiType type1, PsiType type2) {
994     if (isDoubleType(type1)) return unbox(type1);
995     if (isDoubleType(type2)) return unbox(type2);
996     if (isFloatType(type1)) return unbox(type1);
997     if (isFloatType(type2)) return unbox(type2);
998     if (isLongType(type1)) return unbox(type1);
999     if (isLongType(type2)) return unbox(type2);
1000
1001     return PsiType.INT;
1002   }
1003
1004   private static PsiType unbox(PsiType type) {
1005     if (type instanceof PsiPrimitiveType) return type;
1006     if (type instanceof PsiClassType) {
1007       type = PsiPrimitiveType.getUnboxedType(type);
1008       LOG.assertTrue(type != null);
1009       return type;
1010     }
1011     LOG.error("Invalid type for unboxing "+type);
1012     return type;
1013   }
1014
1015   private static final Set<String> INTEGER_NUMBER_TYPES = new THashSet<String>(5);
1016
1017   static {
1018     INTEGER_NUMBER_TYPES.add(PsiType.BYTE.getCanonicalText());
1019     INTEGER_NUMBER_TYPES.add(PsiType.CHAR.getCanonicalText());
1020     INTEGER_NUMBER_TYPES.add(PsiType.LONG.getCanonicalText());
1021     INTEGER_NUMBER_TYPES.add(PsiType.INT.getCanonicalText());
1022     INTEGER_NUMBER_TYPES.add(PsiType.SHORT.getCanonicalText());
1023   }
1024
1025   private static final Set<String> PRIMITIVE_TYPES = new THashSet<String>(9);
1026
1027   static {
1028     PRIMITIVE_TYPES.add(PsiType.VOID.getCanonicalText());
1029     PRIMITIVE_TYPES.add(PsiType.BYTE.getCanonicalText());
1030     PRIMITIVE_TYPES.add(PsiType.CHAR.getCanonicalText());
1031     PRIMITIVE_TYPES.add(PsiType.DOUBLE.getCanonicalText());
1032     PRIMITIVE_TYPES.add(PsiType.FLOAT.getCanonicalText());
1033     PRIMITIVE_TYPES.add(PsiType.LONG.getCanonicalText());
1034     PRIMITIVE_TYPES.add(PsiType.INT.getCanonicalText());
1035     PRIMITIVE_TYPES.add(PsiType.SHORT.getCanonicalText());
1036     PRIMITIVE_TYPES.add(PsiType.BOOLEAN.getCanonicalText());
1037   }
1038
1039   private static final Set<String> PRIMITIVE_WRAPPER_TYPES = new THashSet<String>(8);
1040
1041   static {
1042     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Byte");
1043     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Character");
1044     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Double");
1045     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Float");
1046     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Long");
1047     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Integer");
1048     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Short");
1049     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Boolean");
1050   }
1051
1052   public static boolean isIntegerNumber(String typeName) {
1053     return INTEGER_NUMBER_TYPES.contains(typeName);
1054   }
1055
1056   public static boolean isPrimitive(String typeName) {
1057     return PRIMITIVE_TYPES.contains(typeName);
1058   }
1059
1060   public static boolean isPrimitiveWrapper(String typeName) {
1061     return PRIMITIVE_WRAPPER_TYPES.contains(typeName);
1062   }
1063   public static boolean isPrimitiveWrapper(final PsiType type) {
1064     return type != null && isPrimitiveWrapper(type.getCanonicalText());
1065   }
1066
1067   public static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter) {
1068     return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY);
1069   }
1070
1071   private static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter, final PsiSubstitutor beforeSubstitutor) {
1072     final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
1073     if (extendsList.length > 0) {
1074       final PsiClass psiClass = extendsList[0].resolve();
1075       if (psiClass instanceof PsiTypeParameter) {
1076         Set<PsiClass> visited = new THashSet<PsiClass>();
1077         visited.add(psiClass);
1078         final PsiTypeParameter boundTypeParameter = (PsiTypeParameter)psiClass;
1079         if (beforeSubstitutor.getSubstitutionMap().containsKey(boundTypeParameter)) {
1080           return erasure(beforeSubstitutor.substitute(boundTypeParameter));
1081         }
1082         return typeParameterErasureInner(boundTypeParameter, visited);
1083       }
1084       else if (psiClass != null) {
1085         return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
1086       }
1087     }
1088     return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
1089   }
1090
1091   private static PsiClassType typeParameterErasureInner(PsiTypeParameter typeParameter, Set<PsiClass> visited) {
1092     final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
1093     if (extendsList.length > 0) {
1094       final PsiClass psiClass = extendsList[0].resolve();
1095       if (psiClass instanceof PsiTypeParameter) {
1096         if (!visited.contains(psiClass)) {
1097           visited.add(psiClass);
1098           return typeParameterErasureInner((PsiTypeParameter)psiClass, visited);
1099         }
1100       }
1101       else if (psiClass != null) {
1102         return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
1103       }
1104     }
1105     return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
1106   }
1107
1108   public static PsiType erasure(PsiType type) {
1109     return erasure(type, PsiSubstitutor.EMPTY);
1110   }
1111
1112   public static PsiType erasure(PsiType type, final PsiSubstitutor beforeSubstitutor) {
1113     if (type == null) return null;
1114     return type.accept(new PsiTypeVisitor<PsiType>() {
1115       public PsiType visitClassType(PsiClassType classType) {
1116         final PsiClass aClass = classType.resolve();
1117         if (aClass instanceof PsiTypeParameter) {
1118           return typeParameterErasure((PsiTypeParameter)aClass, beforeSubstitutor);
1119         }
1120         else {
1121           return classType.rawType();
1122         }
1123       }
1124
1125       public PsiType visitWildcardType(PsiWildcardType wildcardType) {
1126         return wildcardType.getExtendsBound().accept(this);
1127       }
1128
1129       public PsiType visitPrimitiveType(PsiPrimitiveType primitiveType) {
1130         return primitiveType;
1131       }
1132
1133       public PsiType visitEllipsisType(PsiEllipsisType ellipsisType) {
1134         final PsiType componentType = ellipsisType.getComponentType();
1135         final PsiType newComponentType = componentType.accept(this);
1136         if (newComponentType == componentType) return ellipsisType;
1137         return new PsiArrayType(newComponentType);
1138       }
1139
1140       public PsiType visitArrayType(PsiArrayType arrayType) {
1141         final PsiType componentType = arrayType.getComponentType();
1142         final PsiType newComponentType = componentType.accept(this);
1143         if (newComponentType == componentType) return arrayType;
1144         return newComponentType.createArrayType();
1145       }
1146     });
1147   }
1148
1149   public static Object computeCastTo(final Object operand, final PsiType castType) {
1150     if (operand == null || castType == null) return null;
1151     Object value;
1152     if (operand instanceof String && castType.equalsToText("java.lang.String")) {
1153       value = operand;
1154     }
1155     else if (operand instanceof Boolean && PsiType.BOOLEAN.equals(castType)) {
1156       value = operand;
1157     }
1158     else {
1159       final PsiType primitiveType = wrapperToPrimitive(operand);
1160       if (primitiveType == null) return null;
1161       // identity cast, including (boolean)boolValue
1162       if (castType.equals(primitiveType)) return operand;
1163       final int rankFrom = getTypeRank(primitiveType);
1164       if (rankFrom > caster.length) return null;
1165       final int rankTo = getTypeRank(castType);
1166       if (rankTo > caster.length) return null;
1167
1168       value = caster[rankFrom - 1][rankTo - 1].cast(operand);
1169     }
1170     return value;
1171   }
1172
1173   public static PsiType unboxAndBalanceTypes(PsiType type1, PsiType type2) {
1174     if (type1 instanceof PsiClassType) type1 = PsiPrimitiveType.getUnboxedType(type1);
1175     if (type2 instanceof PsiClassType) type2 = PsiPrimitiveType.getUnboxedType(type2);
1176
1177     if (PsiType.DOUBLE.equals(type1) || PsiType.DOUBLE.equals(type2)) return PsiType.DOUBLE;
1178     if (PsiType.FLOAT.equals(type1) || PsiType.FLOAT.equals(type2)) return PsiType.FLOAT;
1179     if (PsiType.LONG.equals(type1) || PsiType.LONG.equals(type2)) return PsiType.LONG;
1180     return PsiType.INT;
1181   }
1182
1183   public static IElementType convertEQtoOperation(IElementType eqOpSign) {
1184     IElementType opSign = null;
1185     if (eqOpSign == JavaTokenType.ANDEQ) {
1186       opSign = JavaTokenType.AND;
1187     }
1188     else if (eqOpSign == JavaTokenType.ASTERISKEQ) {
1189       opSign = JavaTokenType.ASTERISK;
1190     }
1191     else if (eqOpSign == JavaTokenType.DIVEQ) {
1192       opSign = JavaTokenType.DIV;
1193     }
1194     else if (eqOpSign == JavaTokenType.GTGTEQ) {
1195       opSign = JavaTokenType.GTGT;
1196     }
1197     else if (eqOpSign == JavaTokenType.GTGTGTEQ) {
1198       opSign = JavaTokenType.GTGTGT;
1199     }
1200     else if (eqOpSign == JavaTokenType.LTLTEQ) {
1201       opSign = JavaTokenType.LTLT;
1202     }
1203     else if (eqOpSign == JavaTokenType.MINUSEQ) {
1204       opSign = JavaTokenType.MINUS;
1205     }
1206     else if (eqOpSign == JavaTokenType.OREQ) {
1207       opSign = JavaTokenType.OR;
1208     }
1209     else if (eqOpSign == JavaTokenType.PERCEQ) {
1210       opSign = JavaTokenType.PERC;
1211     }
1212     else if (eqOpSign == JavaTokenType.PLUSEQ) {
1213       opSign = JavaTokenType.PLUS;
1214     }
1215     else if (eqOpSign == JavaTokenType.XOREQ) {
1216       opSign = JavaTokenType.XOR;
1217     }
1218     return opSign;
1219   }
1220
1221   private interface Caster {
1222     Object cast(Object operand);
1223   }
1224
1225   private static final Caster[][] caster = {
1226     {
1227       new Caster() {
1228         public Object cast(Object operand) {
1229           return operand;
1230         }
1231       }
1232       , new Caster() {
1233         public Object cast(Object operand) {
1234           return Short.valueOf((short)((Number)operand).intValue());
1235         }
1236       }
1237       , new Caster() {
1238         public Object cast(Object operand) {
1239           return new Character((char) ((Number) operand).intValue());
1240         }
1241       }
1242       , new Caster() {
1243         public Object cast(Object operand) {
1244           return Integer.valueOf(((Number)operand).intValue());
1245         }
1246       }
1247       , new Caster() {
1248         public Object cast(Object operand) {
1249           return Long.valueOf(((Number)operand).intValue());
1250         }
1251       }
1252       , new Caster() {
1253         public Object cast(Object operand) {
1254           return new Float(((Number) operand).intValue());
1255         }
1256       }
1257       , new Caster() {
1258         public Object cast(Object operand) {
1259           return new Double(((Number) operand).intValue());
1260         }
1261       }
1262     }
1263     ,
1264     {
1265       new Caster() {
1266         public Object cast(Object operand) {
1267           return Byte.valueOf((byte)((Short)operand).shortValue());
1268         }
1269       }
1270       , new Caster() {
1271         public Object cast(Object operand) {
1272           return Short.valueOf(((Short)operand).shortValue());
1273         }
1274       }
1275       , new Caster() {
1276         public Object cast(Object operand) {
1277           return new Character((char) ((Short) operand).shortValue());
1278         }
1279       }
1280       , new Caster() {
1281         public Object cast(Object operand) {
1282           return Integer.valueOf(((Short)operand).shortValue());
1283         }
1284       }
1285       , new Caster() {
1286         public Object cast(Object operand) {
1287           return Long.valueOf(((Short)operand).shortValue());
1288         }
1289       }
1290       , new Caster() {
1291         public Object cast(Object operand) {
1292           return new Float(((Short) operand).shortValue());
1293         }
1294       }
1295       , new Caster() {
1296         public Object cast(Object operand) {
1297           return new Double(((Short) operand).shortValue());
1298         }
1299       }
1300     }
1301     ,
1302     {
1303       new Caster() {
1304         public Object cast(Object operand) {
1305           return Byte.valueOf((byte)((Character)operand).charValue());
1306         }
1307       }
1308       , new Caster() {
1309         public Object cast(Object operand) {
1310           return Short.valueOf((short)((Character)operand).charValue());
1311         }
1312       }
1313       , new Caster() {
1314         public Object cast(Object operand) {
1315           return new Character(((Character) operand).charValue());
1316         }
1317       }
1318       , new Caster() {
1319         public Object cast(Object operand) {
1320           return Integer.valueOf(((Character)operand).charValue());
1321         }
1322       }
1323       , new Caster() {
1324         public Object cast(Object operand) {
1325           return Long.valueOf(((Character)operand).charValue());
1326         }
1327       }
1328       , new Caster() {
1329         public Object cast(Object operand) {
1330           return new Float(((Character) operand).charValue());
1331         }
1332       }
1333       , new Caster() {
1334         public Object cast(Object operand) {
1335           return new Double(((Character) operand).charValue());
1336         }
1337       }
1338     }
1339     ,
1340     {
1341       new Caster() {
1342         public Object cast(Object operand) {
1343           return Byte.valueOf((byte)((Integer)operand).intValue());
1344         }
1345       }
1346       , new Caster() {
1347         public Object cast(Object operand) {
1348           return Short.valueOf((short)((Integer)operand).intValue());
1349         }
1350       }
1351       , new Caster() {
1352         public Object cast(Object operand) {
1353           return new Character((char) ((Integer) operand).intValue());
1354         }
1355       }
1356       , new Caster() {
1357         public Object cast(Object operand) {
1358           return Integer.valueOf(((Integer)operand).intValue());
1359         }
1360       }
1361       , new Caster() {
1362         public Object cast(Object operand) {
1363           return Long.valueOf(((Integer)operand).intValue());
1364         }
1365       }
1366       , new Caster() {
1367         public Object cast(Object operand) {
1368           return new Float(((Integer) operand).intValue());
1369         }
1370       }
1371       , new Caster() {
1372         public Object cast(Object operand) {
1373           return new Double(((Integer) operand).intValue());
1374         }
1375       }
1376     }
1377     ,
1378     {
1379       new Caster() {
1380         public Object cast(Object operand) {
1381           return Byte.valueOf((byte)((Long)operand).longValue());
1382         }
1383       }
1384       , new Caster() {
1385         public Object cast(Object operand) {
1386           return Short.valueOf((short)((Long)operand).longValue());
1387         }
1388       }
1389       , new Caster() {
1390         public Object cast(Object operand) {
1391           return new Character((char) ((Long) operand).longValue());
1392         }
1393       }
1394       , new Caster() {
1395         public Object cast(Object operand) {
1396           return Integer.valueOf((int)((Long)operand).longValue());
1397         }
1398       }
1399       , new Caster() {
1400         public Object cast(Object operand) {
1401           return Long.valueOf(((Long)operand).longValue());
1402         }
1403       }
1404       , new Caster() {
1405         public Object cast(Object operand) {
1406           return new Float(((Long) operand).longValue());
1407         }
1408       }
1409       , new Caster() {
1410         public Object cast(Object operand) {
1411           return new Double(((Long) operand).longValue());
1412         }
1413       }
1414     }
1415     ,
1416     {
1417       new Caster() {
1418         public Object cast(Object operand) {
1419           return Byte.valueOf((byte)((Float)operand).floatValue());
1420         }
1421       }
1422       , new Caster() {
1423         public Object cast(Object operand) {
1424           return Short.valueOf((short)((Float)operand).floatValue());
1425         }
1426       }
1427       , new Caster() {
1428         public Object cast(Object operand) {
1429           return new Character((char) ((Float) operand).floatValue());
1430         }
1431       }
1432       , new Caster() {
1433         public Object cast(Object operand) {
1434           return Integer.valueOf((int)((Float)operand).floatValue());
1435         }
1436       }
1437       , new Caster() {
1438         public Object cast(Object operand) {
1439           return Long.valueOf((long)((Float)operand).floatValue());
1440         }
1441       }
1442       , new Caster() {
1443         public Object cast(Object operand) {
1444           return new Float(((Float) operand).floatValue());
1445         }
1446       }
1447       , new Caster() {
1448         public Object cast(Object operand) {
1449           return new Double(((Float) operand).floatValue());
1450         }
1451       }
1452     }
1453     ,
1454     {
1455       new Caster() {
1456         public Object cast(Object operand) {
1457           return Byte.valueOf((byte)((Double)operand).doubleValue());
1458         }
1459       }
1460       , new Caster() {
1461         public Object cast(Object operand) {
1462           return Short.valueOf((short)((Double)operand).doubleValue());
1463         }
1464       }
1465       , new Caster() {
1466         public Object cast(Object operand) {
1467           return new Character((char) ((Double) operand).doubleValue());
1468         }
1469       }
1470       , new Caster() {
1471         public Object cast(Object operand) {
1472           return Integer.valueOf((int)((Double)operand).doubleValue());
1473         }
1474       }
1475       , new Caster() {
1476         public Object cast(Object operand) {
1477           return Long.valueOf((long)((Double)operand).doubleValue());
1478         }
1479       }
1480       , new Caster() {
1481         public Object cast(Object operand) {
1482           return new Float(((Double) operand).doubleValue());
1483         }
1484       }
1485       , new Caster() {
1486         public Object cast(Object operand) {
1487           return new Double(((Double) operand).doubleValue());
1488         }
1489       }
1490     }
1491   };
1492
1493   private static final Map<Class, PsiType> WRAPPER_TO_PRIMITIVE = new THashMap<Class, PsiType>(8);
1494   static {
1495     WRAPPER_TO_PRIMITIVE.put(Boolean.class, PsiType.BOOLEAN);
1496     WRAPPER_TO_PRIMITIVE.put(Byte.class, PsiType.BYTE);
1497     WRAPPER_TO_PRIMITIVE.put(Character.class, PsiType.CHAR);
1498     WRAPPER_TO_PRIMITIVE.put(Short.class, PsiType.SHORT);
1499     WRAPPER_TO_PRIMITIVE.put(Integer.class, PsiType.INT);
1500     WRAPPER_TO_PRIMITIVE.put(Long.class, PsiType.LONG);
1501     WRAPPER_TO_PRIMITIVE.put(Float.class, PsiType.FLOAT);
1502     WRAPPER_TO_PRIMITIVE.put(Double.class, PsiType.DOUBLE);
1503   }
1504
1505   private static PsiType wrapperToPrimitive(Object o) {
1506     return WRAPPER_TO_PRIMITIVE.get(o.getClass());
1507   }
1508
1509   @Nullable
1510   public static ClassCandidateInfo splitType(@Nullable PsiType type, @NotNull PsiElement place) {
1511     if (type instanceof PsiArrayType) {
1512       LanguageLevel languageLevel = PsiUtil.getLanguageLevel(place);
1513       final PsiClass arrayClass = JavaPsiFacade.getInstance(place.getProject()).getElementFactory().getArrayClass(languageLevel);
1514       final PsiTypeParameter[] arrayTypeParameters = arrayClass.getTypeParameters();
1515       if (arrayTypeParameters.length > 0) {
1516         return new ClassCandidateInfo(arrayClass, PsiSubstitutor.EMPTY.put(arrayTypeParameters[0], ((PsiArrayType)type).getComponentType()));
1517       }
1518
1519       return new ClassCandidateInfo(arrayClass, PsiSubstitutor.EMPTY);
1520     }
1521
1522     final JavaResolveResult result = PsiUtil.resolveGenericsClassInType(type);
1523     final PsiClass clazz = (PsiClass)result.getElement();
1524     if (clazz == null) {
1525       return null;
1526     }
1527
1528     return new ClassCandidateInfo(clazz, result.getSubstitutor());
1529
1530   }
1531 }