8ccab9517a7b9ec7543778cc8d64a7eaa844f405
[idea/community.git] / java / java-psi-api / src / com / intellij / psi / util / TypeConversionUtil.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 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.ProjectRootModificationTracker;
21 import com.intellij.openapi.util.*;
22 import com.intellij.pom.java.LanguageLevel;
23 import com.intellij.psi.*;
24 import com.intellij.psi.search.GlobalSearchScope;
25 import com.intellij.psi.tree.IElementType;
26 import com.intellij.util.Processor;
27 import com.intellij.util.containers.ContainerUtil;
28 import gnu.trove.THashMap;
29 import gnu.trove.THashSet;
30 import gnu.trove.TObjectIntHashMap;
31 import org.jetbrains.annotations.Contract;
32 import org.jetbrains.annotations.NonNls;
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 import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
42
43 public class TypeConversionUtil {
44   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil");
45
46   private static final boolean[][] IS_ASSIGNABLE_BIT_SET = {
47     {true, true, false, true, true, true, true}, // byte
48     {false, true, false, true, true, true, true}, // short
49     {false, false, true, true, true, true, true}, // char
50     {false, false, false, true, true, true, true}, // int
51     {false, false, false, false, true, true, true}, // long
52     {false, false, false, false, false, true, true}, // float
53     {false, false, false, false, false, false, true}, // double
54   };
55
56   private static final TObjectIntHashMap<PsiType> TYPE_TO_RANK_MAP = new TObjectIntHashMap<PsiType>();
57
58   public static final int BYTE_RANK = 1;
59   public static final int SHORT_RANK = 2;
60   public static final int CHAR_RANK = 3;
61   public static final int INT_RANK = 4;
62   public static final int LONG_RANK = 5;
63   private static final int FLOAT_RANK = 6;
64   private static final int DOUBLE_RANK = 7;
65   private static final int BOOL_RANK = 10;
66   private static final int STRING_RANK = 100;
67   private static final int MAX_NUMERIC_RANK = DOUBLE_RANK;
68   public static final PsiType NULL_TYPE = new PsiEllipsisType(PsiType.NULL){
69     @Override
70     public boolean isValid() {
71       return true;
72     }
73
74     @NotNull
75     @Override
76     @NonNls
77     public String getPresentableText() {
78       return "FAKE TYPE";
79     }
80   };
81   private static final Key<PsiElement> ORIGINAL_CONTEXT = Key.create("ORIGINAL_CONTEXT");
82
83   static {
84     TYPE_TO_RANK_MAP.put(PsiType.BYTE, BYTE_RANK);
85     TYPE_TO_RANK_MAP.put(PsiType.SHORT, SHORT_RANK);
86     TYPE_TO_RANK_MAP.put(PsiType.CHAR, CHAR_RANK);
87     TYPE_TO_RANK_MAP.put(PsiType.INT, INT_RANK);
88     TYPE_TO_RANK_MAP.put(PsiType.LONG, LONG_RANK);
89     TYPE_TO_RANK_MAP.put(PsiType.FLOAT, FLOAT_RANK);
90     TYPE_TO_RANK_MAP.put(PsiType.DOUBLE, DOUBLE_RANK);
91     TYPE_TO_RANK_MAP.put(PsiType.BOOLEAN, BOOL_RANK);
92   }
93
94   private TypeConversionUtil() { }
95
96   /**
97    * @return true if fromType can be casted to toType
98    */
99   public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType) {
100     return areTypesConvertible(fromType, toType, null);
101   }
102
103   /**
104    * @return true if fromType can be casted to toType
105    */
106   public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType, @Nullable LanguageLevel languageLevel) {
107     if (fromType == toType) return true;
108     final boolean fromIsPrimitive = isPrimitiveAndNotNull(fromType);
109     final boolean toIsPrimitive = isPrimitiveAndNotNull(toType);
110     if (fromIsPrimitive || toIsPrimitive) {
111       if (isVoidType(fromType) || isVoidType(toType)) return false;
112       final int fromTypeRank = getTypeRank(fromType);
113       final int toTypeRank = getTypeRank(toType);
114       if (!toIsPrimitive) {
115         if (fromTypeRank == toTypeRank) return true;
116         if (toType instanceof PsiIntersectionType) {
117           for (PsiType type : ((PsiIntersectionType)toType).getConjuncts()) {
118             if (!areTypesConvertible(fromType, type)) return false;
119           }
120           return true;
121         }
122         // JLS 5.5: A value of a primitive type can be cast to a reference type by boxing conversion(see 5.1.7)
123         if (!(toType instanceof PsiClassType)) return false;
124         PsiClass toClass = ((PsiClassType)toType).resolve();
125         if (toClass == null || toClass instanceof PsiTypeParameter) return false;
126         PsiClassType boxedType = ((PsiPrimitiveType)fromType).getBoxedType(toClass.getManager(), toType.getResolveScope());
127         return boxedType != null && areTypesConvertible(boxedType, toType);
128       }
129       if (!fromIsPrimitive) {
130         // 5.5. Casting Contexts
131         if ((fromTypeRank == SHORT_RANK || fromTypeRank == BYTE_RANK) && toTypeRank == CHAR_RANK) return false;
132
133         if (fromType instanceof PsiClassType) {
134           if (languageLevel == null) {
135             languageLevel = ((PsiClassType)fromType).getLanguageLevel();
136           }
137
138           if (languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) {
139             final PsiClassType classType = (PsiClassType)fromType;
140             final PsiClass psiClass = classType.resolve();
141             if (psiClass == null) return false;
142             final PsiClassType boxedType = ((PsiPrimitiveType)toType).getBoxedType(psiClass.getManager(), psiClass.getResolveScope());
143             if (boxedType != null && isNarrowingReferenceConversionAllowed(fromType, boxedType)) {
144               return true;
145             }
146           }
147         }
148         return fromTypeRank == toTypeRank ||
149                fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK && fromTypeRank < toTypeRank;
150       }
151       return fromTypeRank == toTypeRank ||
152              fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK;
153     }
154
155     //type can be casted via widening reference conversion
156     if (isAssignable(toType, fromType)) return true;
157
158     if (isNullType(fromType) || isNullType(toType)) return true;
159
160     // or narrowing reference conversion
161     return isNarrowingReferenceConversionAllowed(fromType, toType);
162   }
163
164   /**
165    * see JLS 5.1.5, JLS3 5.1.6
166    */
167   private static boolean isNarrowingReferenceConversionAllowed(@NotNull PsiType fromType, @NotNull PsiType toType) {
168     if (toType instanceof PsiPrimitiveType || fromType instanceof PsiPrimitiveType) return fromType.equals(toType);
169     //Done with primitives
170     if (toType instanceof PsiDiamondType || fromType instanceof PsiDiamondType) return false;
171     if (toType instanceof PsiArrayType && !(fromType instanceof PsiArrayType)) {
172       if (fromType instanceof PsiClassType) {
173         final PsiClass resolved = ((PsiClassType)fromType).resolve();
174         if (resolved instanceof PsiTypeParameter) {
175           for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
176             if (!isNarrowingReferenceConversionAllowed(boundType, toType)) return false;
177           }
178           return true;
179         }
180       }
181       if (fromType instanceof PsiCapturedWildcardType) {
182         return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getUpperBound(), toType);
183       }
184       return isAssignable(fromType, toType);
185     }
186     if (fromType instanceof PsiArrayType) {
187       if (toType instanceof PsiClassType) {
188         final PsiClass resolved = ((PsiClassType)toType).resolve();
189         if (resolved instanceof PsiTypeParameter) {
190           for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
191             if (!areTypesConvertible(fromType, boundType)) return false;
192           }
193           return true;
194         }
195       }
196       return toType instanceof PsiArrayType
197              && isNarrowingReferenceConversionAllowed(((PsiArrayType)fromType).getComponentType(),
198                                                       ((PsiArrayType)toType).getComponentType());
199     }
200     //Done with array types
201
202     if (fromType instanceof PsiIntersectionType) {
203       final PsiType[] conjuncts = ((PsiIntersectionType)fromType).getConjuncts();
204       for (PsiType conjunct : conjuncts) {
205         if (isNarrowingReferenceConversionAllowed(conjunct, toType)) return true;
206       }
207       return false;
208     }
209     else if (toType instanceof PsiIntersectionType) {
210       if (fromType instanceof PsiClassType && ((PsiClassType)fromType).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_8)) {
211         for (PsiType conjunct : ((PsiIntersectionType)toType).getConjuncts()) {
212           if (!isNarrowingReferenceConversionAllowed(fromType, conjunct)) return false;
213         }
214         return true;
215       }
216       return false;
217     }
218
219     if (fromType instanceof PsiDisjunctionType) {
220       return isNarrowingReferenceConversionAllowed(((PsiDisjunctionType)fromType).getLeastUpperBound(), toType);
221     }
222     if (toType instanceof PsiDisjunctionType) {
223       return false;
224     }
225
226     if (fromType instanceof PsiWildcardType) {
227       final PsiWildcardType fromWildcard = (PsiWildcardType)fromType;
228       final PsiType bound = fromWildcard.getBound();
229       if (bound == null) return true;
230       if (fromWildcard.isSuper()) {
231         return isAssignable(toType, bound);
232       }
233       return isNarrowingReferenceConversionAllowed(bound, toType);
234     }
235     if (toType instanceof PsiWildcardType) {
236       final PsiWildcardType toWildcard = (PsiWildcardType)toType;
237       if (toWildcard.isSuper()) return false;
238       final PsiType bound = toWildcard.getBound();
239       return bound == null || isNarrowingReferenceConversionAllowed(fromType, bound);
240     }
241
242     if (toType instanceof PsiCapturedWildcardType) {
243       return isNarrowingReferenceConversionAllowed(fromType, ((PsiCapturedWildcardType)toType).getUpperBound());
244     }
245     if (fromType instanceof PsiCapturedWildcardType) {
246       return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getUpperBound(), toType);
247     }
248
249     if (isAssignable(fromType, toType)) return true;
250
251     if (!(fromType instanceof PsiClassType) || !(toType instanceof PsiClassType)) return false;
252     PsiClassType fromClassType = (PsiClassType)fromType;
253     PsiClassType toClassType = (PsiClassType)toType;
254
255     PsiClassType.ClassResolveResult fromResult = fromClassType.resolveGenerics();
256     final PsiClass fromClass = fromResult.getElement();
257     if (fromClass == null) return false;
258     if (fromClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(obtainSafeSuperType((PsiTypeParameter)fromClass), toType);
259
260     PsiClassType.ClassResolveResult toResult = toClassType.resolveGenerics();
261     final PsiClass toClass = toResult.getElement();
262     if (toClass == null) return false;
263     if (toClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(fromType, obtainSafeSuperType((PsiTypeParameter)toClass));
264     //Done with type parameters
265
266     PsiManager manager = fromClass.getManager();
267     final LanguageLevel languageLevel = toClassType.getLanguageLevel();
268     if (!fromClass.isInterface()) {
269       if (toClass.isInterface()) {
270         return (!fromClass.hasModifierProperty(PsiModifier.FINAL) || fromClass.isInheritor(toClass, true))&&
271                checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel);
272       }
273       else {
274         if (manager.areElementsEquivalent(fromClass, toClass)) {
275           return areSameParameterTypes(fromClassType, toClassType);
276         }
277
278         if (toClass.isInheritor(fromClass, true)) {
279           return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel);
280         }
281         else if (fromClass.isInheritor(toClass, true)) {
282           return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel);
283         }
284
285         return false;
286       }
287     }
288     else if (!toClass.isInterface()) {
289       if (!toClass.hasModifierProperty(PsiModifier.FINAL)) {
290         return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel);
291       }
292       else {
293         PsiSubstitutor toSubstitutor = getMaybeSuperClassSubstitutor(fromClass, toClass, toResult.getSubstitutor(), null);
294         return toSubstitutor != null && areSameArgumentTypes(fromClass, fromResult.getSubstitutor(), toSubstitutor);
295       }
296     }
297     else if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) {
298       //In jls2 check for method in both interfaces with the same signature but different return types.
299       Collection<HierarchicalMethodSignature> fromClassMethodSignatures = fromClass.getVisibleSignatures();
300       Collection<HierarchicalMethodSignature> toClassMethodSignatures = toClass.getVisibleSignatures();
301
302       for (HierarchicalMethodSignature fromMethodSignature : fromClassMethodSignatures) {
303         for (HierarchicalMethodSignature toMethodSignature : toClassMethodSignatures) {
304           if (fromMethodSignature.equals(toMethodSignature)) {
305             final PsiType fromClassReturnType = fromMethodSignature.getMethod().getReturnType();
306             final PsiType toClassReturnType = toMethodSignature.getMethod().getReturnType();
307             if (fromClassReturnType != null
308                 && toClassReturnType != null
309                 && !fromClassReturnType.equals(toClassReturnType)) {
310               return false;
311             }
312           }
313         }
314       }
315       return true;
316     }
317     else {
318       //In jls3 check for super interface with distinct type arguments
319       PsiClassType.ClassResolveResult baseResult;
320       PsiClass derived;
321       PsiSubstitutor derivedSubstitutor;
322       if (toClass.isInheritor(fromClass, true)) {
323         baseResult = fromResult;
324         derived = toClass;
325         derivedSubstitutor = toResult.getSubstitutor();
326       }
327       else {
328         baseResult = toResult;
329         derived = fromClass;
330         derivedSubstitutor = fromResult.getSubstitutor();
331       }
332       return checkSuperTypesWithDifferentTypeArguments(baseResult, derived, manager, derivedSubstitutor, null, languageLevel);
333     }
334   }
335
336   @NotNull
337   private static PsiClassType obtainSafeSuperType(@NotNull PsiTypeParameter typeParameter) {
338     final PsiClassType superType = typeParameter.getSuperTypes()[0];
339     final PsiClassType.ClassResolveResult result = superType.resolveGenerics();
340     final PsiClass superClass = result.getElement();
341     if (superClass != null) {
342       final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, null);
343       return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(superClass, substitutor);
344     }
345     return superType;
346   }
347
348   private static boolean checkSuperTypesWithDifferentTypeArguments(@NotNull PsiClassType.ClassResolveResult baseResult,
349                                                                    @NotNull PsiClass derived,
350                                                                    @NotNull PsiManager manager,
351                                                                    @NotNull PsiSubstitutor derivedSubstitutor,
352                                                                    Set<PsiClass> visited,
353                                                                    @NotNull LanguageLevel languageLevel) {
354     if (visited != null && visited.contains(derived)) return true;
355
356     if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) return true;
357     PsiClass base = baseResult.getElement();
358     PsiClass[] supers = derived.getSupers();
359     if (manager.areElementsEquivalent(base, derived)) {
360       derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
361       return areSameArgumentTypes(derived, baseResult.getSubstitutor(), derivedSubstitutor, 1);
362     }
363     else {
364       PsiSubstitutor baseSubstitutor = getMaybeSuperClassSubstitutor(derived, base, baseResult.getSubstitutor(), null);
365       if (baseSubstitutor != null) {
366         derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
367         if (!areSameArgumentTypes(derived, baseSubstitutor, derivedSubstitutor)) return false;
368       }
369     }
370
371     if (visited == null) visited = new THashSet<PsiClass>();
372     visited.add(derived);
373     for (PsiClass aSuper : supers) {
374       PsiSubstitutor s = getSuperClassSubstitutor(aSuper, derived, derivedSubstitutor);
375       if (!checkSuperTypesWithDifferentTypeArguments(baseResult, aSuper, manager, s, visited, languageLevel)) return false;
376     }
377
378     return true;
379   }
380
381   private static boolean areSameParameterTypes(@NotNull PsiClassType type1, @NotNull PsiClassType type2) {
382     PsiClassType.ClassResolveResult resolveResult1 = type1.resolveGenerics();
383     PsiClassType.ClassResolveResult resolveResult2 = type2.resolveGenerics();
384     final PsiClass aClass = resolveResult1.getElement();
385     final PsiClass bClass = resolveResult2.getElement();
386     return aClass != null &&
387            bClass != null &&
388            aClass.getManager().areElementsEquivalent(aClass, bClass) &&
389            areSameArgumentTypes(aClass, resolveResult1.getSubstitutor(), resolveResult2.getSubstitutor(), 1);
390   }
391
392   private static boolean areSameArgumentTypes(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor1, @NotNull PsiSubstitutor substitutor2) {
393     return areSameArgumentTypes(aClass, substitutor1, substitutor2, 0);
394   }
395
396   private static boolean areSameArgumentTypes(@NotNull PsiClass aClass,
397                                               @NotNull PsiSubstitutor substitutor1,
398                                               @NotNull PsiSubstitutor substitutor2,
399                                               int level) {
400     for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) {
401       PsiType typeArg1 = substitutor1.substitute(typeParameter);
402       PsiType typeArg2 = substitutor2.substitute(typeParameter);
403       if (typeArg1 == null || typeArg2 == null) return true;
404       if (TypesDistinctProver.provablyDistinct(typeArg1, typeArg2, level)) return false;
405     }
406
407     return true;
408   }
409
410   public static boolean isPrimitiveAndNotNull(PsiType type) {
411     if (type instanceof PsiCapturedWildcardType) {
412       return isPrimitiveAndNotNull(((PsiCapturedWildcardType)type).getUpperBound());
413     }
414     return type instanceof PsiPrimitiveType && !isNullType(type);
415   }
416
417   public static boolean isEnumType(PsiType type) {
418     if (type instanceof PsiCapturedWildcardType) {
419       return isEnumType(((PsiCapturedWildcardType)type).getUpperBound());
420     }
421     if (type instanceof PsiClassType) {
422       final PsiClass psiClass = ((PsiClassType)type).resolve();
423       return psiClass != null && psiClass.isEnum();
424     }
425     return false;
426   }
427
428   public static boolean isNullType(PsiType type) {
429     return PsiType.NULL.equals(type);
430   }
431
432   public static boolean isFloatOrDoubleType(PsiType type) {
433     return isFloatType(type) || isDoubleType(type);
434   }
435   public static boolean isDoubleType(PsiType type) {
436     if (type instanceof PsiCapturedWildcardType) {
437       return isDoubleType(((PsiCapturedWildcardType)type).getUpperBound());
438     }
439     return PsiType.DOUBLE.equals(type) || PsiType.DOUBLE.equals(PsiPrimitiveType.getUnboxedType(type));
440   }
441
442   public static boolean isFloatType(PsiType type) {
443     if (type instanceof PsiCapturedWildcardType) {
444       return isFloatType(((PsiCapturedWildcardType)type).getUpperBound());
445     }
446     return PsiType.FLOAT.equals(type) || PsiType.FLOAT.equals(PsiPrimitiveType.getUnboxedType(type));
447   }
448
449   public static boolean isLongType(PsiType type) {
450     if (type instanceof PsiCapturedWildcardType) {
451       return isLongType(((PsiCapturedWildcardType)type).getUpperBound());
452     }
453     return PsiType.LONG.equals(type) || PsiType.LONG.equals(PsiPrimitiveType.getUnboxedType(type));
454   }
455
456   public static boolean isVoidType(PsiType type) {
457     return PsiType.VOID.equals(type);
458   }
459
460   public static boolean isBooleanType(@Nullable PsiType type) {
461     if (type instanceof PsiCapturedWildcardType) {
462       return isBooleanType(((PsiCapturedWildcardType)type).getUpperBound());
463     }
464     return PsiType.BOOLEAN.equals(type) || PsiType.BOOLEAN.equals(PsiPrimitiveType.getUnboxedType(type));
465   }
466
467   public static boolean isNumericType(int typeRank) {
468     return typeRank <= MAX_NUMERIC_RANK;
469   }
470   public static boolean isNumericType(PsiType type) {
471     return type != null && isNumericType(getTypeRank(type));
472   }
473
474   /**
475    * @return 1..MAX_NUMERIC_TYPE if type is primitive numeric type,
476    *         BOOL_TYPE for boolean,
477    *         STRING_TYPE for String,
478    *         Integer.MAX_VALUE for others
479    */
480   public static int getTypeRank(@NotNull PsiType type) {
481     if (type instanceof PsiCapturedWildcardType) {
482       type = ((PsiCapturedWildcardType)type).getUpperBound();
483     }
484     PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
485     if (unboxedType != null) {
486       type = unboxedType;
487     }
488
489     int rank = TYPE_TO_RANK_MAP.get(type);
490     if (rank != 0) return rank;
491     if (type.equalsToText(JAVA_LANG_STRING)) return STRING_RANK;
492     return Integer.MAX_VALUE;
493   }
494
495   /**
496    * @param tokenType JavaTokenType enumeration
497    * @param strict    true if operator result type should be convertible to the left operand
498    * @return true if lOperand operator rOperand expression is syntactically correct
499    */
500   public static boolean isBinaryOperatorApplicable(IElementType tokenType,
501                                                    PsiExpression lOperand,
502                                                    PsiExpression rOperand,
503                                                    boolean strict) {
504     if (lOperand == null || rOperand == null) return true;
505     final PsiType ltype = lOperand.getType();
506     final PsiType rtype = rOperand.getType();
507     return isBinaryOperatorApplicable(tokenType, ltype, rtype, strict);
508   }
509
510   public static boolean isBinaryOperatorApplicable(final IElementType tokenType, final PsiType ltype, final PsiType rtype, final boolean strict) {
511     if (ltype == null || rtype == null) return true;
512     int resultTypeRank = BOOL_RANK;
513     boolean isApplicable = false;
514     final int ltypeRank = getTypeRank(ltype);
515     final int rtypeRank = getTypeRank(rtype);
516     Label:
517     if (tokenType == JavaTokenType.LT || tokenType == JavaTokenType.LE || tokenType == JavaTokenType.GT || tokenType == JavaTokenType.GE) {
518       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
519         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
520       }
521     }
522     else if (tokenType == JavaTokenType.EQEQ || tokenType == JavaTokenType.NE) {
523       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype) &&
524           (isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype))) {
525         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK
526                        || ltypeRank == BOOL_RANK && rtypeRank == BOOL_RANK;
527       }
528       else {
529         if (isPrimitiveAndNotNull(ltype)) {
530           if (rtype instanceof PsiClassType) {
531             final LanguageLevel languageLevel = ((PsiClassType)rtype).getLanguageLevel();
532             if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) &&
533                 !languageLevel.isAtLeast(LanguageLevel.JDK_1_8) &&
534                 areTypesConvertible(ltype, rtype)) {
535               return true;
536             }
537           }
538           return false;
539         }
540         if (isPrimitiveAndNotNull(rtype)) {
541           if (ltype instanceof PsiClassType) {
542             final LanguageLevel level = ((PsiClassType)ltype).getLanguageLevel();
543             if (level.isAtLeast(LanguageLevel.JDK_1_7) && !level.isAtLeast(LanguageLevel.JDK_1_8) && areTypesConvertible(rtype, ltype)) {
544               return true;
545             }
546           }
547           return false;
548         }
549         isApplicable = areTypesConvertible(ltype, rtype) || areTypesConvertible(rtype, ltype);
550       }
551     }
552     else if (tokenType == JavaTokenType.PLUS) {
553       if (ltype.equalsToText(JAVA_LANG_STRING)) {
554         isApplicable = !isVoidType(rtype);
555         resultTypeRank = STRING_RANK;
556         break Label;
557       }
558       else if (rtype.equalsToText(JAVA_LANG_STRING)) {
559         isApplicable = !isVoidType(ltype);
560         resultTypeRank = STRING_RANK;
561         break Label;
562       }
563       //fallthrough
564
565
566
567
568       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
569         resultTypeRank = Math.max(ltypeRank, rtypeRank);
570         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
571       }
572     }
573     else if (tokenType == JavaTokenType.ASTERISK || tokenType == JavaTokenType.DIV || tokenType == JavaTokenType.PERC ||
574              tokenType == JavaTokenType.MINUS) {
575       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
576         resultTypeRank = Math.max(ltypeRank, rtypeRank);
577         isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
578       }
579     }
580     else if (tokenType == JavaTokenType.LTLT || tokenType == JavaTokenType.GTGT || tokenType == JavaTokenType.GTGTGT) {
581       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
582         isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK;
583         resultTypeRank = INT_RANK;
584       }
585     }
586     else if (tokenType == JavaTokenType.AND || tokenType == JavaTokenType.OR || tokenType == JavaTokenType.XOR) {
587       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
588         isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK
589                        || isBooleanType(ltype) && isBooleanType(rtype);
590         resultTypeRank = ltypeRank <= LONG_RANK ? INT_RANK : BOOL_RANK;
591       }
592     }
593     else if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) {
594       if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
595         isApplicable = isBooleanType(ltype) && isBooleanType(rtype);
596       }
597     }
598     if (isApplicable && strict) {
599       if (resultTypeRank > MAX_NUMERIC_RANK) {
600         isApplicable = ltypeRank == resultTypeRank || ltype.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
601       }
602       else {
603         isApplicable = ltypeRank <= MAX_NUMERIC_RANK;
604       }
605     }
606     return isApplicable;
607   }
608
609   public static boolean isPrimitiveAndNotNullOrWrapper(PsiType type) {
610     if (type instanceof PsiCapturedWildcardType) {
611       return isPrimitiveAndNotNullOrWrapper(((PsiCapturedWildcardType)type).getUpperBound());
612     }
613     if (type instanceof PsiClassType) {
614       return PsiPrimitiveType.getUnboxedType(type) != null;
615     }
616
617     return isPrimitiveAndNotNull(type);
618   }
619
620   public static boolean isUnaryOperatorApplicable(@NotNull PsiJavaToken token, PsiExpression operand) {
621     if (operand == null) return false;
622     PsiType type = operand.getType();
623     return type != null && isUnaryOperatorApplicable(token, type);
624   }
625
626   public static boolean isUnaryOperatorApplicable(@NotNull PsiJavaToken token, @NotNull PsiType type) {
627     IElementType i = token.getTokenType();
628     int typeRank = getTypeRank(type);
629     if (i == JavaTokenType.MINUSMINUS || i == JavaTokenType.PLUSPLUS) {
630       return typeRank <= MAX_NUMERIC_RANK;
631     }
632     if (i == JavaTokenType.MINUS || i == JavaTokenType.PLUS) {
633       return typeRank <= MAX_NUMERIC_RANK;
634     }
635     if (i == JavaTokenType.TILDE) {
636       return typeRank <= LONG_RANK;
637     }
638     if (i == JavaTokenType.EXCL) {
639       return typeRank == BOOL_RANK;
640     }
641     LOG.error("unknown token: " + token);
642     return true;
643   }
644
645   /**
646    * @return true if expression can be the left part of assignment operator
647    */
648   public static boolean isLValue(PsiExpression element) {
649     if (element instanceof PsiReferenceExpression) {
650       final PsiReferenceExpression expression = (PsiReferenceExpression)element;
651       final PsiElement resolved = expression.resolve();
652       return resolved instanceof PsiVariable;
653     }
654     if (element instanceof PsiParenthesizedExpression) {
655       return isLValue(((PsiParenthesizedExpression)element).getExpression());
656     }
657     if (element instanceof PsiArrayAccessExpression) {
658       final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)element;
659       final PsiExpression arrayExpression = arrayAccessExpression.getArrayExpression();
660       final PsiType type = arrayExpression.getType();
661       if (type == null || !(type instanceof PsiArrayType)) return false;
662       final PsiExpression indexExpression = arrayAccessExpression.getIndexExpression();
663       if (indexExpression == null) return false;
664       final PsiType indexType = indexExpression.getType();
665       if (indexType == null) return false;
666       if (getTypeRank(indexType) <= INT_RANK) return true;
667     }
668     return false;
669   }
670
671
672   /**
673    * JLS 5.2
674    */
675   public static boolean areTypesAssignmentCompatible(PsiType lType, PsiExpression rExpr) {
676     if (lType == null || rExpr == null) return true;
677     PsiType rType = rExpr.getType();
678     if (rType == null) return false;
679     if (isAssignable(lType, rType)) return true;
680     if (lType instanceof PsiClassType) {
681         lType = PsiPrimitiveType.getUnboxedType(lType);
682         if (lType == null) return false;
683     }
684
685     final int rTypeRank = getTypeRank(rType);
686     if (lType instanceof PsiPrimitiveType
687         && rType instanceof PsiPrimitiveType
688         && rTypeRank >= BYTE_RANK && rTypeRank <= INT_RANK) {
689       final Object rValue = JavaPsiFacade.getInstance(rExpr.getProject()).getConstantEvaluationHelper().computeConstantExpression(rExpr);
690       final long value;
691       if (rValue instanceof Number) {
692         value = ((Number)rValue).longValue();
693       }
694       else if (rValue instanceof Character) {
695         value = (Character)rValue;
696       }
697       else {
698         return false;
699       }
700
701       if (PsiType.BYTE.equals(lType)) {
702         return -128 <= value && value <= 127;
703       }
704       else if (PsiType.SHORT.equals(lType)) {
705         return -32768 <= value && value <= 32767;
706       }
707       else if (PsiType.CHAR.equals(lType)) {
708         return 0 <= value && value <= 0xFFFF;
709       }
710     }
711     return false;
712   }
713
714   /**
715    * Checks whether values of one type can be assigned to another
716    *
717    * @param left  type to assign to
718    * @param right type of value
719    * @return true if value of type <code>right</code> can be assigned to an l-value of
720    *         type <code>left</code>
721    */
722   public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right) {
723     return isAssignable(left, right, true);
724   }
725
726   public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right, boolean allowUncheckedConversion) {
727     return isAssignable(left, right, allowUncheckedConversion, true);
728   }
729
730   private static boolean isAssignable(@NotNull PsiType left,
731                                       @NotNull PsiType right,
732                                       boolean allowUncheckedConversion,
733                                       boolean capture) {
734     if (left == right || left.equals(right)) return true;
735
736     if (isNullType(right)) {
737       return !(left instanceof PsiPrimitiveType) || isNullType(left);
738     }
739
740     if (right instanceof PsiMethodReferenceType) {
741       final PsiMethodReferenceExpression methodReferenceExpression = ((PsiMethodReferenceType)right).getExpression();
742       if (left instanceof PsiLambdaExpressionType) {
743         final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType();
744         final PsiType lType = ((PsiLambdaExpressionType)left).getExpression().getFunctionalInterfaceType();
745         return Comparing.equal(rType, lType);
746       } else if (left instanceof PsiMethodReferenceType) {
747         final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType();
748         final PsiType lType = ((PsiMethodReferenceType)left).getExpression().getFunctionalInterfaceType();
749         return Comparing.equal(rType, lType);
750       }
751       return !(left instanceof PsiArrayType) && methodReferenceExpression.isAcceptable(left);
752     }
753     if (right instanceof PsiLambdaExpressionType) {
754       final PsiLambdaExpression rLambdaExpression = ((PsiLambdaExpressionType)right).getExpression();
755       if (left instanceof PsiLambdaExpressionType) {
756         final PsiLambdaExpression lLambdaExpression = ((PsiLambdaExpressionType)left).getExpression();
757         final PsiType rType = rLambdaExpression.getFunctionalInterfaceType();
758         final PsiType lType = lLambdaExpression.getFunctionalInterfaceType();
759         return Comparing.equal(rType, lType);
760       }
761       return !(left instanceof PsiArrayType) && rLambdaExpression.isAcceptable(left);
762     }
763
764     if (left instanceof PsiIntersectionType) {
765       PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts();
766       for (PsiType conjunct : conjuncts) {
767         if (!isAssignable(conjunct, right, allowUncheckedConversion, capture)) return false;
768       }
769       return true;
770     }
771     if (right instanceof PsiIntersectionType) {
772       PsiType[] conjuncts = ((PsiIntersectionType)right).getConjuncts();
773       for (PsiType conjunct : conjuncts) {
774         if (isAssignable(left, conjunct, allowUncheckedConversion, capture)) return true;
775       }
776       return false;
777     }
778
779     if (right instanceof PsiCapturedWildcardType) {
780       return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion, capture);
781     }
782
783     if (left instanceof PsiCapturedWildcardType) {
784       return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion, capture);
785     }
786
787     if (left instanceof PsiWildcardType) {
788       return isAssignableToWildcard((PsiWildcardType)left, right);
789     }
790     if (right instanceof PsiWildcardType) {
791       return isAssignableFromWildcard(left, (PsiWildcardType)right);
792     }
793     if (right instanceof PsiArrayType) {
794       if (!(left instanceof PsiArrayType)) {
795         if (left instanceof PsiPrimitiveType || PsiUtil.resolveClassInType(left) == null) return false;
796         PsiClass lClass = PsiUtil.resolveClassInType(left);
797         if (lClass == null) return false;
798         if (lClass.isInterface()) {
799           final String qualifiedName = lClass.getQualifiedName();
800           return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName);
801         }
802         else {
803           return left.equalsToText(CommonClassNames.JAVA_LANG_OBJECT);
804         }
805       }
806       PsiType lCompType = ((PsiArrayType)left).getComponentType();
807       PsiType rCompType = ((PsiArrayType)right).getComponentType();
808       if (lCompType instanceof PsiPrimitiveType) {
809         return lCompType.equals(rCompType);
810       }
811       return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion, capture);
812     }
813
814     if (left instanceof PsiDisjunctionType) {
815       for (PsiType type : ((PsiDisjunctionType)left).getDisjunctions()) {
816         if (isAssignable(type, right, allowUncheckedConversion, capture)) return true;
817       }
818       return false;
819     }
820     if (right instanceof PsiDisjunctionType) {
821       return isAssignable(left, ((PsiDisjunctionType)right).getLeastUpperBound(), allowUncheckedConversion, capture);
822     }
823
824     if (left instanceof PsiArrayType) return false;
825     if (right instanceof PsiPrimitiveType) {
826       if (isVoidType(right)) return false;
827       if (!(left instanceof PsiPrimitiveType)) {
828         return left instanceof PsiClassType && isBoxable((PsiClassType)left, (PsiPrimitiveType)right);
829       }
830       int leftTypeIndex = TYPE_TO_RANK_MAP.get(left) - 1;
831       int rightTypeIndex = TYPE_TO_RANK_MAP.get(right) - 1;
832       return leftTypeIndex >= 0 &&
833              rightTypeIndex >= 0 &&
834              rightTypeIndex < IS_ASSIGNABLE_BIT_SET.length &&
835              leftTypeIndex < IS_ASSIGNABLE_BIT_SET.length &&
836              IS_ASSIGNABLE_BIT_SET[rightTypeIndex][leftTypeIndex];
837     }
838     if (!(right instanceof PsiClassType)) {
839       return false; // must be TypeCook's PsiTypeVariable
840     }
841     if (left instanceof PsiPrimitiveType) {
842       return isUnboxable((PsiPrimitiveType)left, (PsiClassType)right);
843     }
844     final PsiClassType.ClassResolveResult leftResult = PsiUtil.resolveGenericsClassInType(left);
845     final PsiClassType.ClassResolveResult rightResult = PsiUtil.resolveGenericsClassInType(right);
846     if (leftResult.getElement() == null || rightResult.getElement() == null) {
847       if (leftResult.getElement() != rightResult.getElement()) return false;
848       // let's suppose 2 unknown classes, which could be the same to be the same
849       String lText = left.getPresentableText();
850       String rText = right.getPresentableText();
851       if (lText.equals(rText)) return true;
852       if (lText.length() > rText.length() && lText.endsWith(rText) &&
853           lText.charAt(lText.length() - rText.length() - 1) == '.') {
854         return true;
855       }
856       return rText.length() > lText.length()
857              && rText.endsWith(lText)
858              && rText.charAt(rText.length() - lText.length() - 1) == '.';
859     }
860     return isClassAssignable(leftResult, rightResult, allowUncheckedConversion, left.getResolveScope(), capture);
861   }
862
863   private static boolean isAssignableFromWildcard(@NotNull PsiType left, @NotNull PsiWildcardType rightWildcardType) {
864     if (rightWildcardType.isSuper()) {
865       final PsiClass aClass = PsiUtil.resolveClassInType(rightWildcardType.getSuperBound());
866       if (aClass instanceof PsiTypeParameter) {
867         final PsiClassType[] types = aClass.getExtendsListTypes();
868         for (PsiClassType type : types) {
869           if (isAssignable(left, type)) return true;
870         }
871       }
872     }
873     return isAssignable(left, rightWildcardType.getExtendsBound());
874   }
875
876   private static boolean isAssignableToWildcard(@NotNull PsiWildcardType wildcardType, @NotNull PsiType right) {
877     if (wildcardType.isSuper()) {
878       return isAssignable(wildcardType.getSuperBound(), right);
879     }
880     return isAssignable(wildcardType.getExtendsBound(), right);
881   }
882
883   private static boolean isUnboxable(@NotNull PsiPrimitiveType left, @NotNull PsiClassType right) {
884     final PsiPrimitiveType rightUnboxedType = PsiPrimitiveType.getUnboxedType(right);
885     return rightUnboxedType != null && isAssignable(left, rightUnboxedType);
886   }
887
888   public static boolean boxingConversionApplicable(final PsiType left, final PsiType right) {
889     if (left instanceof PsiPrimitiveType && !PsiType.NULL.equals(left)) {
890       return right instanceof PsiClassType && isAssignable(left, right);
891     }
892
893     if (left instanceof PsiIntersectionType) {
894       for (PsiType lConjunct : ((PsiIntersectionType)left).getConjuncts()) {
895         if (!boxingConversionApplicable(lConjunct, right)) return false;
896       }
897       return true;
898     }
899
900     return left instanceof PsiClassType
901               && right instanceof PsiPrimitiveType
902               && !PsiType.NULL.equals(right)
903               && isAssignable(left, right);
904   }
905
906   private static final Key<CachedValue<Set<String>>> POSSIBLE_BOXED_HOLDER_TYPES = Key.create("Types that may be possibly assigned from primitive ones");
907
908   private static boolean isBoxable(@NotNull PsiClassType left, @NotNull PsiPrimitiveType right) {
909     if (!left.getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_5)) return false;
910     final PsiClass psiClass = left.resolve();
911     if (psiClass == null) return false;
912
913     final String qname = psiClass.getQualifiedName();
914     if (qname == null || !(psiClass instanceof PsiTypeParameter || getAllBoxedTypeSupers(psiClass).contains(qname))) {
915       return false;
916     }
917
918     final PsiClassType rightBoxed = right.getBoxedType(psiClass.getManager(), left.getResolveScope());
919     return rightBoxed != null && isAssignable(left, rightBoxed);
920   }
921
922   @NotNull
923   private static Set<String> getAllBoxedTypeSupers(@NotNull PsiClass psiClass) {
924     PsiManager manager = psiClass.getManager();
925     final Project project = psiClass.getProject();
926     CachedValue<Set<String>> boxedHolderTypes = project.getUserData(POSSIBLE_BOXED_HOLDER_TYPES);
927     if (boxedHolderTypes == null) {
928       project.putUserData(POSSIBLE_BOXED_HOLDER_TYPES, boxedHolderTypes = CachedValuesManager.getManager(manager.getProject()).createCachedValue(new CachedValueProvider<Set<String>>() {
929         @Override
930         public Result<Set<String>> compute() {
931           final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
932           final Set<String> set = new THashSet<String>();
933           for (final String qname : PsiPrimitiveType.getAllBoxedTypeNames()) {
934             final PsiClass boxedClass = facade.findClass(qname, GlobalSearchScope.allScope(project));
935             InheritanceUtil.processSupers(boxedClass, true, new Processor<PsiClass>() {
936               @Override
937               public boolean process(PsiClass psiClass) {
938                 ContainerUtil.addIfNotNull(psiClass.getQualifiedName(), set);
939                 return true;
940               }
941             });
942           }
943           return Result.create(set, ProjectRootModificationTracker.getInstance(project));
944         }
945       }, false));
946     }
947
948     return boxedHolderTypes.getValue();
949   }
950
951   private static boolean isClassAssignable(@NotNull PsiClassType.ClassResolveResult leftResult,
952                                            @NotNull PsiClassType.ClassResolveResult rightResult,
953                                            boolean allowUncheckedConversion, 
954                                            GlobalSearchScope resolveScope, 
955                                            boolean capture) {
956     final PsiClass leftClass = leftResult.getElement();
957     final PsiClass rightClass = rightResult.getElement();
958     if (leftClass == null || rightClass == null) return false;
959
960     PsiSubstitutor superSubstitutor = JavaClassSupers.getInstance().getSuperClassSubstitutor(leftClass, rightClass, resolveScope,
961                                                                                              rightResult.getSubstitutor());
962     return superSubstitutor != null && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion, superSubstitutor, capture);
963   }
964
965   private static boolean typeParametersAgree(@NotNull PsiClassType.ClassResolveResult leftResult,
966                                              @NotNull PsiClassType.ClassResolveResult rightResult,
967                                              boolean allowUncheckedConversion, PsiSubstitutor superSubstitutor,
968                                              boolean capture) {
969     PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();
970     PsiClass leftClass = leftResult.getElement();
971     PsiClass rightClass = rightResult.getElement();
972
973     Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
974
975     if (!li.hasNext()) return true;
976     PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();
977
978     if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
979       rightSubstitutor = superSubstitutor;
980       rightClass = leftClass;
981     }
982     else if (!PsiUtil.typeParametersIterator(rightClass).hasNext()) return true;
983
984     Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
985     while (li.hasNext()) {
986       if (!ri.hasNext()) return false;
987       PsiTypeParameter lp = li.next();
988       PsiTypeParameter rp = ri.next();
989       final PsiType typeLeft = leftSubstitutor.substitute(lp);
990       if (typeLeft == null) continue;
991       final PsiType typeRight = PsiCapturedWildcardType.isCapture() && capture
992                                 ? rightSubstitutor.substituteWithBoundsPromotion(rp)
993                                 : rightSubstitutor.substitute(rp);
994       if (typeRight == null) {
995         // compatibility feature: allow to assign raw types to generic ones
996         return allowUncheckedConversion;
997       }
998       if (!typesAgree(typeLeft, typeRight, allowUncheckedConversion)) {
999         return false;
1000       }
1001     }
1002     return true;
1003   }
1004
1005   private static final RecursionGuard ourGuard = RecursionManager.createGuard("isAssignable");
1006
1007   public static boolean typesAgree(final @NotNull PsiType typeLeft,
1008                                    final @NotNull PsiType typeRight,
1009                                    final boolean allowUncheckedConversion) {
1010     if (typeLeft instanceof PsiWildcardType) {
1011       final PsiWildcardType leftWildcard = (PsiWildcardType)typeLeft;
1012       final PsiType leftBound = leftWildcard.getBound();
1013       if (leftBound == null) return true;
1014       if (leftBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
1015         if (!leftWildcard.isSuper()) return true;
1016         if (typeRight.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true;
1017       }
1018
1019       if (typeRight instanceof PsiWildcardType) {
1020         final PsiWildcardType rightWildcard = (PsiWildcardType)typeRight;
1021         if (leftWildcard.isExtends()) {
1022           return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), allowUncheckedConversion, false);
1023         }
1024         else { //isSuper
1025           if (rightWildcard.isSuper()) {
1026             final Boolean assignable = ourGuard.doPreventingRecursion(rightWildcard, true, new NotNullComputable<Boolean>() {
1027               @NotNull
1028               @Override
1029               public Boolean compute() {
1030                 return isAssignable(rightWildcard.getBound(), leftBound, allowUncheckedConversion, false);
1031               }
1032             });
1033             if (assignable != null && assignable) {
1034               return true;
1035             }
1036           }
1037           return false;
1038         }
1039       }
1040       else {
1041         if (leftWildcard.isExtends()) {
1042           return isAssignable(leftBound, typeRight, false, false);
1043         }
1044         else { // isSuper
1045           final Boolean assignable = ourGuard.doPreventingRecursion(leftWildcard, true, new NotNullComputable<Boolean>() {
1046             @NotNull
1047             @Override
1048             public Boolean compute() {
1049               return isAssignable(typeRight, leftBound, false, false);
1050             }
1051           });
1052           return assignable == null || assignable.booleanValue(); 
1053         }
1054       }
1055     }
1056     else {
1057       return typeLeft.equals(typeRight);
1058     }
1059   }
1060
1061   @Nullable
1062   public static PsiSubstitutor getClassSubstitutor(@NotNull PsiClass superClassCandidate,
1063                                                    @NotNull PsiClass derivedClassCandidate,
1064                                                    @NotNull PsiSubstitutor derivedSubstitutor) {
1065     if (superClassCandidate.getManager().areElementsEquivalent(superClassCandidate, derivedClassCandidate)) {
1066       PsiTypeParameter[] baseParams = superClassCandidate.getTypeParameters();
1067       PsiTypeParameter[] derivedParams = derivedClassCandidate.getTypeParameters();
1068       if (baseParams.length > 0 && derivedParams.length == 0) {
1069         return JavaPsiFacade.getInstance(superClassCandidate.getProject()).getElementFactory().createRawSubstitutor(superClassCandidate);
1070       }
1071       return derivedSubstitutor;
1072     }
1073     return getMaybeSuperClassSubstitutor(superClassCandidate, derivedClassCandidate, derivedSubstitutor, null);
1074   }
1075
1076   private static final Set<String> ourReportedSuperClassSubstitutorExceptions = ContainerUtil.newConcurrentSet();
1077
1078   /**
1079    * Calculates substitutor that binds type parameters in <code>superClass</code> with
1080    * values that they have in <code>derivedClass</code>, given that type parameters in
1081    * <code>derivedClass</code> are bound by <code>derivedSubstitutor</code>.
1082    * <code>superClass</code> must be a super class/interface of <code>derivedClass</code> (as in
1083    * <code>InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true)</code>
1084    *
1085    * @return substitutor (never returns <code>null</code>)
1086    * @see PsiClass#isInheritor(PsiClass, boolean)
1087    * @see InheritanceUtil#isInheritorOrSelf(PsiClass, PsiClass, boolean)
1088    */
1089   @NotNull
1090   public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass,
1091                                                         @NotNull PsiClass derivedClass,
1092                                                         @NotNull PsiSubstitutor derivedSubstitutor) {
1093     if (!superClass.hasTypeParameters() && superClass.getContainingClass() == null) return PsiSubstitutor.EMPTY; //optimization and protection against EJB queer hierarchy
1094
1095     Set<PsiClass> visited = new THashSet<PsiClass>();
1096     PsiSubstitutor substitutor = getMaybeSuperClassSubstitutor(superClass, derivedClass, derivedSubstitutor, visited);
1097
1098     if (substitutor == null) {
1099       if (ourReportedSuperClassSubstitutorExceptions.add(derivedClass.getQualifiedName() + "/" + superClass.getQualifiedName())) {
1100         reportHierarchyInconsistency(superClass, derivedClass, visited);
1101       }
1102       return PsiSubstitutor.EMPTY;
1103     }
1104     return substitutor;
1105   }
1106
1107   // the same as getSuperClassSubstitutor() but can return null, which means that classes were not inheritors
1108   @Nullable
1109   public static PsiSubstitutor getMaybeSuperClassSubstitutor(@NotNull PsiClass superClass,
1110                                                              @NotNull PsiClass derivedClass,
1111                                                              @NotNull PsiSubstitutor derivedSubstitutor,
1112                                                              @Nullable Set<PsiClass> visited) {
1113     return JavaClassSupers.getInstance().getSuperClassSubstitutor(superClass, derivedClass, derivedClass.getResolveScope(), derivedSubstitutor);
1114   }
1115
1116   private static void reportHierarchyInconsistency(@NotNull PsiClass superClass, @NotNull PsiClass derivedClass, @NotNull Set<PsiClass> visited) {
1117     final StringBuilder msg = new StringBuilder("Super: " + classInfo(superClass));
1118     msg.append("visited:\n");
1119     for (PsiClass aClass : visited) {
1120       msg.append("  each: " + classInfo(aClass));
1121     }
1122     msg.append("isInheritor: " + InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true) + " " + derivedClass.isInheritor(superClass, true));
1123     msg.append("\nhierarchy:\n");
1124     InheritanceUtil.processSupers(derivedClass, true, new Processor<PsiClass>() {
1125       @Override
1126       public boolean process(PsiClass psiClass) {
1127         msg.append("each: " + classInfo(psiClass));
1128         return true;
1129       }
1130     });
1131     LOG.error(msg.toString());
1132   }
1133
1134   @NotNull
1135   private static String classInfo(@NotNull PsiClass aClass) {
1136     String s = aClass.getQualifiedName() + "(" + aClass.getClass().getName() + "; " + PsiUtilCore.getVirtualFile(aClass) + ");\n";
1137     s += "extends: ";
1138     for (PsiClassType type : aClass.getExtendsListTypes()) {
1139       s += type + " (" + type.getClass().getName() + "; " + type.resolve() + ") ";
1140     }
1141     s += "\nimplements: ";
1142     for (PsiClassType type : aClass.getImplementsListTypes()) {
1143       s += type + " (" + type.getClass().getName() + "; " + type.resolve() + ") ";
1144     }
1145     return s + "\n";
1146   }
1147
1148   @NotNull
1149   public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, @NotNull PsiClassType classType) {
1150       final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
1151       return getSuperClassSubstitutor(superClass, classResolveResult.getElement(), classResolveResult.getSubstitutor());
1152   }
1153
1154   /**
1155    * see JLS 5.6.2
1156    */
1157   @NotNull
1158   public static PsiType binaryNumericPromotion(PsiType type1, PsiType type2) {
1159     if (isDoubleType(type1)) return unbox(type1);
1160     if (isDoubleType(type2)) return unbox(type2);
1161     if (isFloatType(type1)) return unbox(type1);
1162     if (isFloatType(type2)) return unbox(type2);
1163     if (isLongType(type1)) return unbox(type1);
1164     if (isLongType(type2)) return unbox(type2);
1165
1166     return PsiType.INT;
1167   }
1168
1169   @NotNull
1170   private static PsiType unbox(@NotNull PsiType type) {
1171     if (type instanceof PsiPrimitiveType) return type;
1172     if (type instanceof PsiClassType) {
1173       type = PsiPrimitiveType.getUnboxedType(type);
1174       LOG.assertTrue(type != null);
1175       return type;
1176     }
1177     LOG.error("Invalid type for unboxing "+type);
1178     return type;
1179   }
1180
1181   private static final Set<String> INTEGER_NUMBER_TYPES = new THashSet<String>(5);
1182
1183   static {
1184     INTEGER_NUMBER_TYPES.add(PsiType.BYTE.getCanonicalText());
1185     INTEGER_NUMBER_TYPES.add(PsiType.CHAR.getCanonicalText());
1186     INTEGER_NUMBER_TYPES.add(PsiType.LONG.getCanonicalText());
1187     INTEGER_NUMBER_TYPES.add(PsiType.INT.getCanonicalText());
1188     INTEGER_NUMBER_TYPES.add(PsiType.SHORT.getCanonicalText());
1189   }
1190
1191   private static final Set<String> PRIMITIVE_TYPES = new THashSet<String>(9);
1192
1193   static {
1194     PRIMITIVE_TYPES.add(PsiType.VOID.getCanonicalText());
1195     PRIMITIVE_TYPES.add(PsiType.BYTE.getCanonicalText());
1196     PRIMITIVE_TYPES.add(PsiType.CHAR.getCanonicalText());
1197     PRIMITIVE_TYPES.add(PsiType.DOUBLE.getCanonicalText());
1198     PRIMITIVE_TYPES.add(PsiType.FLOAT.getCanonicalText());
1199     PRIMITIVE_TYPES.add(PsiType.LONG.getCanonicalText());
1200     PRIMITIVE_TYPES.add(PsiType.INT.getCanonicalText());
1201     PRIMITIVE_TYPES.add(PsiType.SHORT.getCanonicalText());
1202     PRIMITIVE_TYPES.add(PsiType.BOOLEAN.getCanonicalText());
1203   }
1204
1205   private static final Set<String> PRIMITIVE_WRAPPER_TYPES = new THashSet<String>(8);
1206
1207   static {
1208     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Byte");
1209     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Character");
1210     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Double");
1211     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Float");
1212     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Long");
1213     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Integer");
1214     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Short");
1215     PRIMITIVE_WRAPPER_TYPES.add("java.lang.Boolean");
1216   }
1217
1218   public static boolean isIntegerNumber(String typeName) {
1219     return INTEGER_NUMBER_TYPES.contains(typeName);
1220   }
1221
1222   public static boolean isPrimitive(String typeName) {
1223     return PRIMITIVE_TYPES.contains(typeName);
1224   }
1225
1226   public static boolean isPrimitiveWrapper(String typeName) {
1227     return PRIMITIVE_WRAPPER_TYPES.contains(typeName);
1228   }
1229   @Contract("null -> false")
1230   public static boolean isAssignableFromPrimitiveWrapper(final PsiType type) {
1231     if (type == null) return false;
1232     return isPrimitiveWrapper(type) ||
1233            type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) ||
1234            type.equalsToText(CommonClassNames.JAVA_LANG_NUMBER);
1235   }
1236
1237   @Contract("null -> false")
1238   public static boolean isPrimitiveWrapper(final PsiType type) {
1239     return type != null && isPrimitiveWrapper(type.getCanonicalText());
1240   }
1241
1242   @Contract("null -> false")
1243   public static boolean isComposite(final PsiType type) {
1244     return type instanceof PsiDisjunctionType || type instanceof PsiIntersectionType;
1245   }
1246
1247   public static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter) {
1248     return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY);
1249   }
1250
1251   private static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter, @NotNull PsiSubstitutor beforeSubstitutor) {
1252     final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
1253     if (extendsList.length > 0) {
1254       final PsiClass psiClass = extendsList[0].resolve();
1255       if (psiClass instanceof PsiTypeParameter) {
1256         Set<PsiClass> visited = new THashSet<PsiClass>();
1257         visited.add(psiClass);
1258         final PsiTypeParameter boundTypeParameter = (PsiTypeParameter)psiClass;
1259         if (beforeSubstitutor.getSubstitutionMap().containsKey(boundTypeParameter)) {
1260           return erasure(beforeSubstitutor.substitute(boundTypeParameter));
1261         }
1262         return typeParameterErasureInner(boundTypeParameter, visited, beforeSubstitutor);
1263       }
1264       else if (psiClass != null) {
1265         return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
1266       }
1267     }
1268     return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
1269   }
1270
1271   private static PsiClassType typeParameterErasureInner(PsiTypeParameter typeParameter,
1272                                                         Set<PsiClass> visited,
1273                                                         PsiSubstitutor beforeSubstitutor) {
1274     final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
1275     if (extendsList.length > 0) {
1276       final PsiClass psiClass = extendsList[0].resolve();
1277       if (psiClass instanceof PsiTypeParameter) {
1278         if (!visited.contains(psiClass)) {
1279           visited.add(psiClass);
1280           if (beforeSubstitutor.getSubstitutionMap().containsKey(psiClass)) {
1281             return (PsiClassType)erasure(beforeSubstitutor.substitute((PsiTypeParameter)psiClass));
1282           }
1283           return typeParameterErasureInner((PsiTypeParameter)psiClass, visited, beforeSubstitutor);
1284         }
1285       }
1286       else if (psiClass != null) {
1287         return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass);
1288       }
1289     }
1290     return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
1291   }
1292
1293   @Contract("null -> null")
1294   public static PsiType erasure(@Nullable PsiType type) {
1295     return erasure(type, PsiSubstitutor.EMPTY);
1296   }
1297
1298   @Contract("null, _ -> null")
1299   public static PsiType erasure(@Nullable final PsiType type, @NotNull final PsiSubstitutor beforeSubstitutor) {
1300     if (type == null) return null;
1301     return type.accept(new PsiTypeVisitor<PsiType>() {
1302       @Nullable
1303       @Override
1304       public PsiType visitType(PsiType type) {
1305         return type;
1306       }
1307
1308       @Override
1309       public PsiType visitClassType(PsiClassType classType) {
1310         final PsiClass aClass = classType.resolve();
1311         if (aClass instanceof PsiTypeParameter && !isFreshVariable((PsiTypeParameter)aClass)) {
1312           return typeParameterErasure((PsiTypeParameter)aClass, beforeSubstitutor);
1313         }
1314         return classType.rawType();
1315       }
1316
1317       @Override
1318       public PsiType visitWildcardType(PsiWildcardType wildcardType) {
1319         return wildcardType;
1320       }
1321
1322       @Nullable
1323       @Override
1324       public PsiType visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) {
1325         return capturedWildcardType.getUpperBound().accept(this);
1326       }
1327
1328       @Override
1329       public PsiType visitPrimitiveType(PsiPrimitiveType primitiveType) {
1330         return primitiveType;
1331       }
1332
1333       @Override
1334       public PsiType visitEllipsisType(PsiEllipsisType ellipsisType) {
1335         final PsiType componentType = ellipsisType.getComponentType();
1336         final PsiType newComponentType = componentType.accept(this);
1337         if (newComponentType == componentType) return ellipsisType;
1338         return newComponentType != null ? newComponentType.createArrayType() : null;
1339       }
1340
1341       @Override
1342       public PsiType visitArrayType(PsiArrayType arrayType) {
1343         final PsiType componentType = arrayType.getComponentType();
1344         final PsiType newComponentType = componentType.accept(this);
1345         if (newComponentType == componentType) return arrayType;
1346         return newComponentType != null ? newComponentType.createArrayType() : null;
1347       }
1348
1349       @Override
1350       public PsiType visitDisjunctionType(PsiDisjunctionType disjunctionType) {
1351         final PsiClassType lub = PsiTypesUtil.getLowestUpperBoundClassType(disjunctionType);
1352         return lub != null ? erasure(lub, beforeSubstitutor) : disjunctionType;
1353       }
1354     });
1355   }
1356
1357   public static Object computeCastTo(final Object operand, final PsiType castType) {
1358     if (operand == null || castType == null) return null;
1359     Object value;
1360     if (operand instanceof String && castType.equalsToText(JAVA_LANG_STRING)) {
1361       value = operand;
1362     }
1363     else if (operand instanceof Boolean && PsiType.BOOLEAN.equals(castType)) {
1364       value = operand;
1365     }
1366     else {
1367       final PsiType primitiveType = wrapperToPrimitive(operand);
1368       if (primitiveType == null) return null;
1369       // identity cast, including (boolean)boolValue
1370       if (castType.equals(primitiveType)) return operand;
1371       final int rankFrom = getTypeRank(primitiveType);
1372       if (rankFrom > caster.length) return null;
1373       final int rankTo = getTypeRank(castType);
1374       if (rankTo > caster.length) return null;
1375
1376       value = caster[rankFrom - 1][rankTo - 1].cast(operand);
1377     }
1378     return value;
1379   }
1380
1381   @NotNull
1382   public static PsiType unboxAndBalanceTypes(PsiType type1, PsiType type2) {
1383     if (type1 instanceof PsiClassType) type1 = PsiPrimitiveType.getUnboxedType(type1);
1384     if (type2 instanceof PsiClassType) type2 = PsiPrimitiveType.getUnboxedType(type2);
1385
1386     if (PsiType.DOUBLE.equals(type1) || PsiType.DOUBLE.equals(type2)) return PsiType.DOUBLE;
1387     if (PsiType.FLOAT.equals(type1) || PsiType.FLOAT.equals(type2)) return PsiType.FLOAT;
1388     if (PsiType.LONG.equals(type1) || PsiType.LONG.equals(type2)) return PsiType.LONG;
1389     return PsiType.INT;
1390   }
1391
1392   public static IElementType convertEQtoOperation(IElementType eqOpSign) {
1393     IElementType opSign = null;
1394     if (eqOpSign == JavaTokenType.ANDEQ) {
1395       opSign = JavaTokenType.AND;
1396     }
1397     else if (eqOpSign == JavaTokenType.ASTERISKEQ) {
1398       opSign = JavaTokenType.ASTERISK;
1399     }
1400     else if (eqOpSign == JavaTokenType.DIVEQ) {
1401       opSign = JavaTokenType.DIV;
1402     }
1403     else if (eqOpSign == JavaTokenType.GTGTEQ) {
1404       opSign = JavaTokenType.GTGT;
1405     }
1406     else if (eqOpSign == JavaTokenType.GTGTGTEQ) {
1407       opSign = JavaTokenType.GTGTGT;
1408     }
1409     else if (eqOpSign == JavaTokenType.LTLTEQ) {
1410       opSign = JavaTokenType.LTLT;
1411     }
1412     else if (eqOpSign == JavaTokenType.MINUSEQ) {
1413       opSign = JavaTokenType.MINUS;
1414     }
1415     else if (eqOpSign == JavaTokenType.OREQ) {
1416       opSign = JavaTokenType.OR;
1417     }
1418     else if (eqOpSign == JavaTokenType.PERCEQ) {
1419       opSign = JavaTokenType.PERC;
1420     }
1421     else if (eqOpSign == JavaTokenType.PLUSEQ) {
1422       opSign = JavaTokenType.PLUS;
1423     }
1424     else if (eqOpSign == JavaTokenType.XOREQ) {
1425       opSign = JavaTokenType.XOR;
1426     }
1427     return opSign;
1428   }
1429
1430   @Nullable
1431   public static PsiType calcTypeForBinaryExpression(PsiType lType, PsiType rType, @NotNull IElementType sign, boolean accessLType) {
1432     if (sign == JavaTokenType.PLUS) {
1433       // evaluate right argument first, since '+-/*%' is left associative and left operand tends to be bigger
1434       if (rType == null) return null;
1435       if (rType.equalsToText(JAVA_LANG_STRING)) {
1436         return rType;
1437       }
1438       if (!accessLType) return NULL_TYPE;
1439       if (lType == null) return null;
1440       if (lType.equalsToText(JAVA_LANG_STRING)) {
1441         return lType;
1442       }
1443       return unboxAndBalanceTypes(lType, rType);
1444     }
1445     if (sign == JavaTokenType.MINUS || sign == JavaTokenType.ASTERISK || sign == JavaTokenType.DIV || sign == JavaTokenType.PERC) {
1446       if (rType == null) return null;
1447       if (!accessLType) return NULL_TYPE;
1448       if (lType == null) return null;
1449       return unboxAndBalanceTypes(lType, rType);
1450     }
1451     if (sign == JavaTokenType.LTLT || sign == JavaTokenType.GTGT || sign == JavaTokenType.GTGTGT) {
1452       if (!accessLType) return NULL_TYPE;
1453       if (PsiType.BYTE.equals(lType) || PsiType.CHAR.equals(lType) || PsiType.SHORT.equals(lType)) {
1454         return PsiType.INT;
1455       }
1456       if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType);
1457       return lType;
1458     }
1459     if (sign == JavaTokenType.EQEQ ||
1460         sign == JavaTokenType.NE ||
1461         sign == JavaTokenType.LT ||
1462         sign == JavaTokenType.GT ||
1463         sign == JavaTokenType.LE ||
1464         sign == JavaTokenType.GE ||
1465         sign == JavaTokenType.OROR ||
1466         sign == JavaTokenType.ANDAND) {
1467       return PsiType.BOOLEAN;
1468     }
1469     if (sign == JavaTokenType.OR || sign == JavaTokenType.XOR || sign == JavaTokenType.AND) {
1470       if (rType instanceof PsiClassType) rType = PsiPrimitiveType.getUnboxedType(rType);
1471
1472       if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType);
1473
1474       if (rType == null) return null;
1475       if (PsiType.BOOLEAN.equals(rType)) return PsiType.BOOLEAN;
1476       if (!accessLType) return NULL_TYPE;
1477       if (lType == null) return null;
1478       if (PsiType.BOOLEAN.equals(lType)) return PsiType.BOOLEAN;
1479       if (PsiType.LONG.equals(lType) || PsiType.LONG.equals(rType)) return PsiType.LONG;
1480       return PsiType.INT;
1481     }
1482     LOG.error("Unknown token: "+sign);
1483     return null;
1484   }
1485
1486   /**
1487    * See JLS 3.10.2. Floating-Point Literals
1488    * @return true  if floating point literal consists of zeros only
1489    */
1490   public static boolean isFPZero(@NotNull final String text) {
1491     for (int i = 0; i < text.length(); i++) {
1492       final char c = text.charAt(i);
1493       if (Character.isDigit(c) && c != '0') return false;
1494       final char d = Character.toUpperCase(c);
1495       if (d == 'E' || d == 'P') break;
1496     }
1497     return true;
1498   }
1499
1500   public static boolean areSameFreshVariables(PsiTypeParameter p1, PsiTypeParameter p2) {
1501     final PsiElement originalContext = p1.getUserData(ORIGINAL_CONTEXT);
1502     return originalContext != null && originalContext == p2.getUserData(ORIGINAL_CONTEXT);
1503   }
1504
1505   public static boolean isFreshVariable(PsiTypeParameter typeParameter) {
1506     return typeParameter.getUserData(ORIGINAL_CONTEXT) != null;
1507   }
1508   
1509   public static void markAsFreshVariable(PsiTypeParameter parameter, PsiElement context) {
1510     parameter.putUserData(ORIGINAL_CONTEXT, context);
1511   }
1512
1513   private interface Caster {
1514     @NotNull
1515     Object cast(@NotNull Object operand);
1516   }
1517
1518   private static final Caster[][] caster = {
1519     {
1520       new Caster() {
1521         @NotNull
1522         @Override
1523         public Object cast(@NotNull Object operand) {
1524           return operand;
1525         }
1526       }
1527       , new Caster() {
1528         @NotNull
1529         @Override
1530         public Object cast(@NotNull Object operand) {
1531           return (short)((Number)operand).intValue();
1532         }
1533       }
1534       , new Caster() {
1535         @NotNull
1536         @Override
1537         public Object cast(@NotNull Object operand) {
1538           return (char)((Number)operand).intValue();
1539         }
1540       }
1541       , new Caster() {
1542         @NotNull
1543         @Override
1544         public Object cast(@NotNull Object operand) {
1545           return ((Number)operand).intValue();
1546         }
1547       }
1548       , new Caster() {
1549         @NotNull
1550         @Override
1551         public Object cast(@NotNull Object operand) {
1552           return (long)((Number)operand).intValue();
1553         }
1554       }
1555       , new Caster() {
1556         @NotNull
1557         @Override
1558         public Object cast(@NotNull Object operand) {
1559           return (float)((Number)operand).intValue();
1560         }
1561       }
1562       , new Caster() {
1563         @NotNull
1564         @Override
1565         public Object cast(@NotNull Object operand) {
1566           return (double)((Number)operand).intValue();
1567         }
1568       }
1569     }
1570     ,
1571     {
1572       new Caster() {
1573         @NotNull
1574         @Override
1575         public Object cast(@NotNull Object operand) {
1576           return (byte)((Short)operand).shortValue();
1577         }
1578       }
1579       , new Caster() {
1580         @NotNull
1581         @Override
1582         public Object cast(@NotNull Object operand) {
1583           return operand;
1584         }
1585       }
1586       , new Caster() {
1587         @NotNull
1588         @Override
1589         public Object cast(@NotNull Object operand) {
1590           return (char)((Short)operand).shortValue();
1591         }
1592       }
1593       , new Caster() {
1594         @NotNull
1595         @Override
1596         public Object cast(@NotNull Object operand) {
1597           return (int)(Short)operand;
1598         }
1599       }
1600       , new Caster() {
1601         @NotNull
1602         @Override
1603         public Object cast(@NotNull Object operand) {
1604           return (long)(Short)operand;
1605         }
1606       }
1607       , new Caster() {
1608         @NotNull
1609         @Override
1610         public Object cast(@NotNull Object operand) {
1611           return (float)(Short)operand;
1612         }
1613       }
1614       , new Caster() {
1615         @NotNull
1616         @Override
1617         public Object cast(@NotNull Object operand) {
1618           return (double)(Short)operand;
1619         }
1620       }
1621     }
1622     ,
1623     {
1624       new Caster() {
1625         @NotNull
1626         @Override
1627         public Object cast(@NotNull Object operand) {
1628           return (byte)((Character)operand).charValue();
1629         }
1630       }
1631       , new Caster() {
1632         @NotNull
1633         @Override
1634         public Object cast(@NotNull Object operand) {
1635           return (short)((Character)operand).charValue();
1636         }
1637       }
1638       , new Caster() {
1639         @NotNull
1640         @Override
1641         public Object cast(@NotNull Object operand) {
1642           return operand;
1643         }
1644       }
1645       , new Caster() {
1646         @NotNull
1647         @Override
1648         public Object cast(@NotNull Object operand) {
1649           return (int)(Character)operand;
1650         }
1651       }
1652       , new Caster() {
1653         @NotNull
1654         @Override
1655         public Object cast(@NotNull Object operand) {
1656           return (long)(Character)operand;
1657         }
1658       }
1659       , new Caster() {
1660         @NotNull
1661         @Override
1662         public Object cast(@NotNull Object operand) {
1663           return (float)(Character)operand;
1664         }
1665       }
1666       , new Caster() {
1667         @NotNull
1668         @Override
1669         public Object cast(@NotNull Object operand) {
1670           return (double)(Character)operand;
1671         }
1672       }
1673     }
1674     ,
1675     {
1676       new Caster() {
1677         @NotNull
1678         @Override
1679         public Object cast(@NotNull Object operand) {
1680           return (byte)((Integer)operand).intValue();
1681         }
1682       }
1683       , new Caster() {
1684         @NotNull
1685         @Override
1686         public Object cast(@NotNull Object operand) {
1687           return (short)((Integer)operand).intValue();
1688         }
1689       }
1690       , new Caster() {
1691         @NotNull
1692         @Override
1693         public Object cast(@NotNull Object operand) {
1694           return (char)((Integer)operand).intValue();
1695         }
1696       }
1697       , new Caster() {
1698         @NotNull
1699         @Override
1700         public Object cast(@NotNull Object operand) {
1701           return operand;
1702         }
1703       }
1704       , new Caster() {
1705         @NotNull
1706         @Override
1707         public Object cast(@NotNull Object operand) {
1708           return (long)(Integer)operand;
1709         }
1710       }
1711       , new Caster() {
1712         @NotNull
1713         @Override
1714         public Object cast(@NotNull Object operand) {
1715           return (float)(Integer)operand;
1716         }
1717       }
1718       , new Caster() {
1719         @NotNull
1720         @Override
1721         public Object cast(@NotNull Object operand) {
1722           return (double)(Integer)operand;
1723         }
1724       }
1725     }
1726     ,
1727     {
1728       new Caster() {
1729         @NotNull
1730         @Override
1731         public Object cast(@NotNull Object operand) {
1732           return (byte)((Long)operand).longValue();
1733         }
1734       }
1735       , new Caster() {
1736         @NotNull
1737         @Override
1738         public Object cast(@NotNull Object operand) {
1739           return (short)((Long)operand).longValue();
1740         }
1741       }
1742       , new Caster() {
1743         @NotNull
1744         @Override
1745         public Object cast(@NotNull Object operand) {
1746           return (char)((Long)operand).longValue();
1747         }
1748       }
1749       , new Caster() {
1750         @NotNull
1751         @Override
1752         public Object cast(@NotNull Object operand) {
1753           return (int)((Long)operand).longValue();
1754         }
1755       }
1756       , new Caster() {
1757         @NotNull
1758         @Override
1759         public Object cast(@NotNull Object operand) {
1760           return operand;
1761         }
1762       }
1763       , new Caster() {
1764         @NotNull
1765         @Override
1766         public Object cast(@NotNull Object operand) {
1767           return (float)(Long)operand;
1768         }
1769       }
1770       , new Caster() {
1771         @NotNull
1772         @Override
1773         public Object cast(@NotNull Object operand) {
1774           return (double)(Long)operand;
1775         }
1776       }
1777     }
1778     ,
1779     {
1780       new Caster() {
1781         @NotNull
1782         @Override
1783         public Object cast(@NotNull Object operand) {
1784           return (byte)((Float)operand).floatValue();
1785         }
1786       }
1787       , new Caster() {
1788         @NotNull
1789         @Override
1790         public Object cast(@NotNull Object operand) {
1791           return (short)((Float)operand).floatValue();
1792         }
1793       }
1794       , new Caster() {
1795         @NotNull
1796         @Override
1797         public Object cast(@NotNull Object operand) {
1798           return (char)((Float)operand).floatValue();
1799         }
1800       }
1801       , new Caster() {
1802         @NotNull
1803         @Override
1804         public Object cast(@NotNull Object operand) {
1805           return (int)((Float)operand).floatValue();
1806         }
1807       }
1808       , new Caster() {
1809         @NotNull
1810         @Override
1811         public Object cast(@NotNull Object operand) {
1812           return (long)((Float)operand).floatValue();
1813         }
1814       }
1815       , new Caster() {
1816         @NotNull
1817         @Override
1818         public Object cast(@NotNull Object operand) {
1819           return operand;
1820         }
1821       }
1822       , new Caster() {
1823         @NotNull
1824         @Override
1825         public Object cast(@NotNull Object operand) {
1826           return (double)(Float)operand;
1827         }
1828       }
1829     }
1830     ,
1831     {
1832       new Caster() {
1833         @NotNull
1834         @Override
1835         public Object cast(@NotNull Object operand) {
1836           return (byte)((Double)operand).doubleValue();
1837         }
1838       }
1839       , new Caster() {
1840         @NotNull
1841         @Override
1842         public Object cast(@NotNull Object operand) {
1843           return (short)((Double)operand).doubleValue();
1844         }
1845       }
1846       , new Caster() {
1847         @NotNull
1848         @Override
1849         public Object cast(@NotNull Object operand) {
1850           return (char)((Double)operand).doubleValue();
1851         }
1852       }
1853       , new Caster() {
1854         @NotNull
1855         @Override
1856         public Object cast(@NotNull Object operand) {
1857           return (int)((Double)operand).doubleValue();
1858         }
1859       }
1860       , new Caster() {
1861         @NotNull
1862         @Override
1863         public Object cast(@NotNull Object operand) {
1864           return (long)((Double)operand).doubleValue();
1865         }
1866       }
1867       , new Caster() {
1868         @NotNull
1869         @Override
1870         public Object cast(@NotNull Object operand) {
1871           return new Float((Double)operand);
1872         }
1873       }
1874       , new Caster() {
1875         @NotNull
1876         @Override
1877         public Object cast(@NotNull Object operand) {
1878           return operand;
1879         }
1880       }
1881     }
1882   };
1883
1884   private static final Map<Class, PsiType> WRAPPER_TO_PRIMITIVE = new THashMap<Class, PsiType>(8);
1885   static {
1886     WRAPPER_TO_PRIMITIVE.put(Boolean.class, PsiType.BOOLEAN);
1887     WRAPPER_TO_PRIMITIVE.put(Byte.class, PsiType.BYTE);
1888     WRAPPER_TO_PRIMITIVE.put(Character.class, PsiType.CHAR);
1889     WRAPPER_TO_PRIMITIVE.put(Short.class, PsiType.SHORT);
1890     WRAPPER_TO_PRIMITIVE.put(Integer.class, PsiType.INT);
1891     WRAPPER_TO_PRIMITIVE.put(Long.class, PsiType.LONG);
1892     WRAPPER_TO_PRIMITIVE.put(Float.class, PsiType.FLOAT);
1893     WRAPPER_TO_PRIMITIVE.put(Double.class, PsiType.DOUBLE);
1894   }
1895
1896   private static PsiType wrapperToPrimitive(@NotNull Object o) {
1897     return WRAPPER_TO_PRIMITIVE.get(o.getClass());
1898   }
1899 }