Package com.intellij.psi.util

Source Code of com.intellij.psi.util.TypeConversionUtil$Caster

/*
* Copyright 2000-2007 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.util;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashMap;
import gnu.trove.THashSet;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class TypeConversionUtil {
  private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil");

  private static final boolean[][] IS_ASSIGNABLE_BIT_SET = new boolean[][]{
    {true, true, false, true, true, true, true}, // byte
    {false, true, false, true, true, true, true}, // short
    {false, false, true, true, true, true, true}, // char
    {false, false, false, true, true, true, true}, // int
    {false, false, false, false, true, true, true}, // long
    {false, false, false, false, false, true, true}, // float
    {false, false, false, false, false, false, true}, // double
  };

  private static final TObjectIntHashMap<PsiType> TYPE_TO_RANK_MAP = new TObjectIntHashMap<PsiType>();

  private static final int BOOL_RANK = 10;
  public static final int BYTE_RANK = 1;
  public static final int SHORT_RANK = 2;
  public static final int CHAR_RANK = 3;
  public static final int INT_RANK = 4;
  private static final int LONG_RANK = 5;
  private static final int FLOAT_RANK = 6;
  private static final int DOUBLE_RANK = 7;
  private static final int STRING_RANK = 100;
  private static final int MAX_NUMERIC_RANK = DOUBLE_RANK;

  static {
    TYPE_TO_RANK_MAP.put(PsiType.BYTE, BYTE_RANK);
    TYPE_TO_RANK_MAP.put(PsiType.SHORT, SHORT_RANK);
    TYPE_TO_RANK_MAP.put(PsiType.CHAR, CHAR_RANK);
    TYPE_TO_RANK_MAP.put(PsiType.INT, INT_RANK);
    TYPE_TO_RANK_MAP.put(PsiType.LONG, LONG_RANK);
    TYPE_TO_RANK_MAP.put(PsiType.FLOAT, FLOAT_RANK);
    TYPE_TO_RANK_MAP.put(PsiType.DOUBLE, DOUBLE_RANK);
  }


  /**
   * @return true iff fromType can be casted to toType
   */
  public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType) {
    if (fromType == toType) return true;
    final boolean fromIsPrimitive = isPrimitiveAndNotNull(fromType);
    final boolean toIsPrimitive = isPrimitiveAndNotNull(toType);
    if (fromIsPrimitive || toIsPrimitive) {
      if (isVoidType(fromType) || isVoidType(toType)) return false;
      final int fromTypeRank = getTypeRank(fromType);
      final int toTypeRank = getTypeRank(toType);
      if (!toIsPrimitive) {
        if (fromTypeRank == toTypeRank) return true;
        // JLS 5.5: A value of a primitive type can be cast to a reference type by boxing conversion(see 5.1.7)
        if (!(toType instanceof PsiClassType)) return false;
        PsiClass toClass = ((PsiClassType)toType).resolve();
        if (toClass == null) return false;
        PsiClassType boxedType = ((PsiPrimitiveType)fromType).getBoxedType(toClass.getManager(), toType.getResolveScope());
        return boxedType != null && areTypesConvertible(boxedType, toType);
      }
      if (!fromIsPrimitive) {
        if (fromTypeRank == toTypeRank) return true;
        return fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK && fromTypeRank < toTypeRank;
      }
      return fromTypeRank == toTypeRank ||
             fromTypeRank <= MAX_NUMERIC_RANK && toTypeRank <= MAX_NUMERIC_RANK;
    }

    //type can be casted via widening reference conversion
    if (isAssignable(toType, fromType)) return true;

    if (isNullType(fromType) || isNullType(toType)) return true;

    // or narrowing reference conversion
    return isNarrowingReferenceConversionAllowed(fromType, toType);
  }

  /**
   * see JLS 5.1.5, JLS3 5.5
   */
  private static boolean isNarrowingReferenceConversionAllowed(PsiType fromType, PsiType toType) {
    if (toType instanceof PsiPrimitiveType || fromType instanceof PsiPrimitiveType) return fromType.equals(toType);
    //Done with primitives

    if (toType instanceof PsiArrayType && !(fromType instanceof PsiArrayType)) {
      if (fromType instanceof PsiClassType) {
        final PsiClass resolved = ((PsiClassType)fromType).resolve();
        if (resolved instanceof PsiTypeParameter) {
          for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
            if (!isNarrowingReferenceConversionAllowed(boundType, toType)) return false;
          }
          return true;
        }
      }
      return isAssignable(fromType, toType);
    }
    if (fromType instanceof PsiArrayType) {
      if (toType instanceof PsiClassType) {
        final PsiClass resolved = ((PsiClassType)toType).resolve();
        if (resolved instanceof PsiTypeParameter) {
          for (final PsiClassType boundType : resolved.getExtendsListTypes()) {
            if (!isNarrowingReferenceConversionAllowed(fromType, boundType)) return false;
          }
          return true;
        }
      }
      return toType instanceof PsiArrayType
             && isNarrowingReferenceConversionAllowed(((PsiArrayType)fromType).getComponentType(),
                                                      ((PsiArrayType)toType).getComponentType());
    }
    //Done with array types

    if (fromType instanceof PsiIntersectionType) {
      final PsiType[] conjuncts = ((PsiIntersectionType)fromType).getConjuncts();
      for (PsiType conjunct : conjuncts) {
        if (isNarrowingReferenceConversionAllowed(conjunct, toType)) return true;
      }
      return false;
    } else if (toType instanceof PsiIntersectionType) return false;

    if (fromType instanceof PsiWildcardType) {
      final PsiWildcardType fromWildcard = (PsiWildcardType)fromType;
      final PsiType bound = fromWildcard.getBound();
      if (bound == null) return true;
      if (fromWildcard.isSuper()) {
        return isAssignable(toType, bound);
      }
      return isNarrowingReferenceConversionAllowed(bound, toType);
    }
    if (toType instanceof PsiWildcardType) {
      final PsiWildcardType toWildcard = (PsiWildcardType)toType;
      if (toWildcard.isSuper()) return false;
      final PsiType bound = toWildcard.getBound();
      return bound == null || isNarrowingReferenceConversionAllowed(fromType, bound);
    }

    if (toType instanceof PsiCapturedWildcardType) {
      return isNarrowingReferenceConversionAllowed(toType, ((PsiCapturedWildcardType)toType).getWildcard());
    }
    if (fromType instanceof PsiCapturedWildcardType) {
      return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getWildcard(), toType);
    }

    if (isAssignable(fromType, toType)) return true;

    LOG.assertTrue(toType instanceof PsiClassType && fromType instanceof PsiClassType);
    PsiClassType fromClassType = (PsiClassType)fromType;
    PsiClassType toClassType = (PsiClassType)toType;

    PsiClassType.ClassResolveResult fromResult = fromClassType.resolveGenerics();
    final PsiClass fromClass = fromResult.getElement();
    if (fromClass == null) return false;
    if (fromClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(obtainSafeSuperType((PsiTypeParameter)fromClass), toType);

    PsiClassType.ClassResolveResult toResult = toClassType.resolveGenerics();
    final PsiClass toClass = toResult.getElement();
    if (toClass == null) return false;
    if (toClass instanceof PsiTypeParameter) return isNarrowingReferenceConversionAllowed(fromType, obtainSafeSuperType((PsiTypeParameter)toClass));
    //Done with type parameters

    PsiManager manager = fromClass.getManager();
    final LanguageLevel languageLevel = toClassType.getLanguageLevel();
    if (!fromClass.isInterface()) {
      if (toClass.isInterface()) {
        return !fromClass.hasModifierProperty(PsiModifier.FINAL) &&
               checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager,
                                                         fromResult.getSubstitutor(),
                                                         new THashSet<PsiClass>(),
                                                         languageLevel);
      }
      else {
        if (manager.areElementsEquivalent(fromClass, toClass)) {
          return !areDistinctParameterTypes(fromClassType, toClassType);
        }

        if (toClass.isInheritor(fromClass, true)) {
          return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), new THashSet<PsiClass>(),
                                                           languageLevel);
        }
        else if (fromClass.isInheritor(toClass, true)) {
          return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), new THashSet<PsiClass>(),
                                                           languageLevel);
        }

        return false;
      }
    }
    else {
      if (!toClass.isInterface()) {
        if (!toClass.hasModifierProperty(PsiModifier.FINAL)) {
          return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), new THashSet<PsiClass>(),
                                                           languageLevel);
        }
        else {
          if (!toClass.isInheritor(fromClass, true)) return false;
          PsiSubstitutor toSubstitutor = getSuperClassSubstitutor(fromClass, toClass, toResult.getSubstitutor());
          return !areDistinctArgumentTypes(fromClass, fromResult.getSubstitutor(), toSubstitutor);
        }
      }
      else {
        if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) {
          //In jls2 check for method in both interfaces with the same signature but different return types.
          Collection<HierarchicalMethodSignature> fromClassMethodSignatures = fromClass.getVisibleSignatures();
          Collection<HierarchicalMethodSignature> toClassMethodSignatures = toClass.getVisibleSignatures();

          for (HierarchicalMethodSignature fromMethodSignature : fromClassMethodSignatures) {
            for (HierarchicalMethodSignature toMethodSignature : toClassMethodSignatures) {
              if (fromMethodSignature.equals(toMethodSignature)) {
                final PsiType fromClassReturnType = fromMethodSignature.getMethod().getReturnType();
                final PsiType toClassReturnType = toMethodSignature.getMethod().getReturnType();
                if (fromClassReturnType != null
                    && toClassReturnType != null
                    && !fromClassReturnType.equals(toClassReturnType)) {
                  return false;
                }
              }
            }
          }
          return true;
        }
        else {
          //In jls3 check for super interface with distinct type arguments
          if (toClass.isInheritor(fromClass, true)) {
            return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), new THashSet<PsiClass>(),
                                                             languageLevel);
          }
          else {
            return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), new THashSet<PsiClass>(),
                                                             languageLevel);
          }
        }
      }
    }
  }

  private static PsiClassType obtainSafeSuperType(final PsiTypeParameter typeParameter) {
    final PsiClassType superType = typeParameter.getSuperTypes()[0];
    final PsiClassType.ClassResolveResult result = superType.resolveGenerics();
    final PsiClass superClass = result.getElement();
    if (superClass != null) {
      final PsiSubstitutor substitutor = result.getSubstitutor().put(typeParameter, null);
      return typeParameter.getManager().getElementFactory().createType(superClass, substitutor);
    }
    return superType;
  }

  private static boolean checkSuperTypesWithDifferentTypeArguments(PsiClassType.ClassResolveResult baseResult,
                                                                   PsiClass derived,
                                                                   PsiManager manager,
                                                                   PsiSubstitutor derivedSubstitutor,
                                                                   final Set<PsiClass> visited,
                                                                   final LanguageLevel languageLevel) {
    if (visited.contains(derived)) return true;
    visited.add(derived);

    if (languageLevel.compareTo(LanguageLevel.JDK_1_5) < 0) return true;
    PsiClass base = baseResult.getElement();
    PsiClass[] supers = derived.getSupers();
    if (manager.areElementsEquivalent(base, derived)) {
      derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
      return !areDistinctArgumentTypes(derived, baseResult.getSubstitutor(), derivedSubstitutor);
    }
    else if (base.isInheritor(derived, true)) {
      derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor);
      PsiSubstitutor baseSubstitutor = getSuperClassSubstitutor(derived, base, baseResult.getSubstitutor());
      if (areDistinctArgumentTypes(derived, baseSubstitutor, derivedSubstitutor)) return false;
    }

    for (PsiClass aSuper : supers) {
      PsiSubstitutor s = getSuperClassSubstitutor(aSuper, derived, derivedSubstitutor);
      if (!checkSuperTypesWithDifferentTypeArguments(baseResult, aSuper, manager, s, visited, languageLevel)) return false;
    }

    return true;
  }

  private static boolean areDistinctParameterTypes(PsiClassType type1, PsiClassType type2) {
    PsiClassType.ClassResolveResult resolveResult1 = type1.resolveGenerics();
    PsiClassType.ClassResolveResult resolveResult2 = type2.resolveGenerics();
    final PsiClass aClass = resolveResult1.getElement();
    final PsiClass bClass = resolveResult2.getElement();
    if (aClass == null || bClass == null) return true;
    if (!aClass.getManager().areElementsEquivalent(aClass, bClass)) return true;
    return areDistinctArgumentTypes(aClass, resolveResult1.getSubstitutor(), resolveResult2.getSubstitutor());
  }

  private static boolean areDistinctArgumentTypes(PsiClass aClass, PsiSubstitutor substitutor1, PsiSubstitutor substitutor2) {
    Iterator<PsiTypeParameter> iterator = PsiUtil.typeParametersIterator(aClass);
    while(iterator.hasNext()) {
      PsiTypeParameter typeParam = iterator.next();
      PsiType typeArg1 = substitutor1.substitute(typeParam);
      PsiType typeArg2 = substitutor2.substitute(typeParam);
      if (typeArg1 == null || typeArg2 == null) return false;
      if (typeArg1 instanceof PsiWildcardType || typeArg2 instanceof PsiWildcardType) return false;
      if (typeArg1 instanceof PsiCapturedWildcardType || typeArg2 instanceof PsiCapturedWildcardType) return false;

      if (typeArg1 instanceof PsiClassType && ((PsiClassType)typeArg1).resolve() instanceof PsiTypeParameter) return false;
      if (typeArg2 instanceof PsiClassType && ((PsiClassType)typeArg2).resolve() instanceof PsiTypeParameter) return false;
      if (!typeArg1.equals(typeArg2)) return true;
    }

    return false;
  }

  public static boolean isPrimitiveAndNotNull(PsiType type) {
    return type instanceof PsiPrimitiveType && !isNullType(type);
  }

  public static boolean isEnumType(PsiType type) {
    if (type instanceof PsiClassType) {
      final PsiClass psiClass = ((PsiClassType)type).resolve();
      return psiClass != null && psiClass.isEnum();
    }
    return false;
  }

  public static boolean isNullType(PsiType type) {
    return PsiType.NULL == type;
  }

  public static boolean isDoubleType(PsiType type) {
    return PsiType.DOUBLE == type || PsiPrimitiveType.getUnboxedType(type) == PsiType.DOUBLE;
  }

  public static boolean isFloatType(PsiType type) {
    return PsiType.FLOAT == type || PsiPrimitiveType.getUnboxedType(type) == PsiType.FLOAT;
  }

  public static boolean isLongType(PsiType type) {
    return PsiType.LONG == type || PsiPrimitiveType.getUnboxedType(type) == PsiType.LONG;
  }

  public static boolean isVoidType(PsiType type) {
    return PsiType.VOID == type;
  }

  public static boolean isBooleanType(PsiType type) {
    return PsiType.BOOLEAN == type || PsiPrimitiveType.getUnboxedType(type) == PsiType.BOOLEAN;
  }

  public static boolean isNumericType(int typeRank) {
    return typeRank <= MAX_NUMERIC_RANK;
  }
  public static boolean isNumericType(PsiType type) {
    return isNumericType(getTypeRank(type));
  }

  /**
   * @return 1..MAX_NUMERIC_TYPE if type is primitive numeric type,
   *         BOOL_TYPE for boolean,
   *         STRING_TYPE for String, Integer.MAX_VALUE for other
   */
  public static int getTypeRank(PsiType type) {
    PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(type);
    if (unboxedType != null) {
      type = unboxedType;
    }

    int rank = TYPE_TO_RANK_MAP.get(type);
    if (rank != 0) return rank;
    if (PsiType.BOOLEAN == type) return BOOL_RANK;
    if (type.equalsToText("java.lang.String")) return STRING_RANK;
    return Integer.MAX_VALUE;
  }

  /**
   * @param tokenType JavaTokenType enumeration
   * @param lOperand
   * @param rOperand
   * @param strict    true if operator result type should be convertible to the left operand
   * @return true if lOperand operator rOperand expression is syntactically correct
   */
  public static boolean isBinaryOperatorApplicable(IElementType tokenType,
                                                   PsiExpression lOperand,
                                                   PsiExpression rOperand,
                                                   boolean strict) {
    if (lOperand == null || rOperand == null) return true;
    final PsiType ltype = lOperand.getType();
    final PsiType rtype = rOperand.getType();
    if (ltype == null || rtype == null) return true;
    int resultTypeRank = BOOL_RANK;
    boolean isApplicable = false;
    final int ltypeRank = getTypeRank(ltype);
    final int rtypeRank = getTypeRank(rtype);
    Label:
    if (tokenType == JavaTokenType.LT || tokenType == JavaTokenType.LE || tokenType == JavaTokenType.GT || tokenType == JavaTokenType.GE) {
      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
        isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
      }
    }
    else if (tokenType == JavaTokenType.EQEQ || tokenType == JavaTokenType.NE) {
      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype) &&
          (isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype))) {
        isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK
                       || ltypeRank == BOOL_RANK && rtypeRank == BOOL_RANK;
      }
      else {
        if (isPrimitiveAndNotNull(ltype) || isPrimitiveAndNotNull(rtype)) return false;
        isApplicable = areTypesConvertible(ltype, rtype) || areTypesConvertible(rtype, ltype);
      }
    }
    else if (tokenType == JavaTokenType.PLUS) {
      if (ltype.equalsToText("java.lang.String")) {
        isApplicable = !isVoidType(rtype);
        resultTypeRank = STRING_RANK;
        break Label;
      }
      else if (rtype.equalsToText("java.lang.String")) {
        isApplicable = !isVoidType(ltype);
        resultTypeRank = STRING_RANK;
        break Label;
      }
      //fallthrough




      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
        resultTypeRank = Math.max(ltypeRank, rtypeRank);
        isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
      }
    }
    else if (tokenType == JavaTokenType.ASTERISK || tokenType == JavaTokenType.DIV || tokenType == JavaTokenType.PERC ||
             tokenType == JavaTokenType.MINUS) {
      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
        resultTypeRank = Math.max(ltypeRank, rtypeRank);
        isApplicable = ltypeRank <= MAX_NUMERIC_RANK && rtypeRank <= MAX_NUMERIC_RANK;
      }
    }
    else if (tokenType == JavaTokenType.LTLT || tokenType == JavaTokenType.GTGT || tokenType == JavaTokenType.GTGTGT) {
      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
        isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK;
        resultTypeRank = INT_RANK;
      }
    }
    else if (tokenType == JavaTokenType.AND || tokenType == JavaTokenType.OR || tokenType == JavaTokenType.XOR) {
      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
        isApplicable = ltypeRank <= LONG_RANK && rtypeRank <= LONG_RANK
                       || isBooleanType(ltype) && isBooleanType(rtype);
        resultTypeRank = ltypeRank <= LONG_RANK ? INT_RANK : BOOL_RANK;
      }
    }
    else if (tokenType == JavaTokenType.ANDAND || tokenType == JavaTokenType.OROR) {
      if (isPrimitiveAndNotNullOrWrapper(ltype) && isPrimitiveAndNotNullOrWrapper(rtype)) {
        isApplicable = isBooleanType(ltype) && isBooleanType(rtype);
      }
    }
    if (isApplicable && strict) {
      if (resultTypeRank > MAX_NUMERIC_RANK) {
        isApplicable = ltypeRank == resultTypeRank || ltype.equalsToText("java.lang.Object");
      }
      else {
        isApplicable = ltypeRank <= MAX_NUMERIC_RANK;
      }
    }
    return isApplicable;
  }

  public static boolean isPrimitiveAndNotNullOrWrapper(PsiType type) {
    if (type instanceof PsiClassType) {
      return PsiPrimitiveType.getUnboxedType(type) != null;
    }

    return isPrimitiveAndNotNull(type);
  }

  public static boolean isUnaryOperatorApplicable(PsiJavaToken token, PsiExpression operand) {
    if (operand == null) return false;
    PsiType type = operand.getType();
    if (type == null) return false;
    IElementType i = token.getTokenType();
    int typeRank = getTypeRank(type);
    if (i == JavaTokenType.MINUSMINUS || i == JavaTokenType.PLUSPLUS) {
      return typeRank <= MAX_NUMERIC_RANK;
    }
    else if (i == JavaTokenType.MINUS || i == JavaTokenType.PLUS) {
      return typeRank <= MAX_NUMERIC_RANK;
    }
    else if (i == JavaTokenType.TILDE) {
      return typeRank <= LONG_RANK;
    }
    else if (i == JavaTokenType.EXCL) {
      return typeRank == BOOL_RANK;
    }
    else {
      LOG.error("unknown token: " + token);
    }
    return true;
  }

  /**
   * @return true if expression can be the left part of assignment operator
   */
  public static boolean isLValue(PsiExpression element) {
    if (element instanceof PsiReferenceExpression) {
      final PsiReferenceExpression expression = (PsiReferenceExpression)element;
      final PsiElement resolved = expression.resolve();
      return resolved instanceof PsiVariable;
    }
    if (element instanceof PsiParenthesizedExpression) {
      return isLValue(((PsiParenthesizedExpression)element).getExpression());
    }
    if (element instanceof PsiArrayAccessExpression) {
      final PsiArrayAccessExpression arrayAccessExpression = (PsiArrayAccessExpression)element;
      final PsiExpression arrayExpression = arrayAccessExpression.getArrayExpression();
      final PsiType type = arrayExpression.getType();
      if (type == null || !(type instanceof PsiArrayType)) return false;
      final PsiExpression indexExpression = arrayAccessExpression.getIndexExpression();
      if (indexExpression == null) return false;
      final PsiType indexType = indexExpression.getType();
      if (indexType == null) return false;
      if (getTypeRank(indexType) <= INT_RANK) return true;
    }
    return false;
  }


  /**
   * JLS 5.2
   */
  public static boolean areTypesAssignmentCompatible(PsiType lType, PsiExpression rExpr) {
    if (lType == null || rExpr == null) return true;
    PsiType rType = rExpr.getType();
    if (rType == null) return false;
    if (isAssignable(lType, rType)) return true;
    if (lType instanceof PsiClassType) {
        lType = PsiPrimitiveType.getUnboxedType(lType);
        if (lType == null) return false;
    }

    final int rTypeRank = getTypeRank(rType);
    if (lType instanceof PsiPrimitiveType
        && rType instanceof PsiPrimitiveType
        && rTypeRank >= BYTE_RANK && rTypeRank <= INT_RANK) {
      final Object rValue = rExpr.getManager().getConstantEvaluationHelper().computeConstantExpression(rExpr);
      final long value;
      if (rValue instanceof Number) {
        value = ((Number)rValue).longValue();
      }
      else if (rValue instanceof Character) {
        value = ((Character)rValue).charValue();
      }
      else {
        return false;
      }

      if (PsiType.BYTE == lType) {
        return -128 <= value && value <= 127;
      }
      else if (PsiType.SHORT == lType) {
        return -32768 <= value && value <= 32767;
      }
      else if (PsiType.CHAR == lType) {
        return 0 <= value && value <= 0xFFFF;
      }
    }
    return false;
  }

  /**
   * Checks whether values of one type can be assigned to another
   *
   * @param left  type to assign to
   * @param right type of value
   * @return true if value of type <code>right</code> can be assigned to an l-value of
   *         type <code>left</code>
   */
  public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right) {
    return isAssignable(left, right, true);
  }


  public static boolean isAssignable(@NotNull PsiType left, @NotNull PsiType right, boolean allowUncheckedConversion) {
    if (left == right) return true;
    if (isNullType(right)) {
      return !(left instanceof PsiPrimitiveType) || isNullType(left);
    }

    if (left instanceof PsiIntersectionType) {
      PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts();
      for (PsiType conjunct : conjuncts) {
        if (!isAssignable(conjunct, right, allowUncheckedConversion)) return false;
      }
      return true;
    } else if (right instanceof PsiIntersectionType) {
      PsiType[] conjuncts = ((PsiIntersectionType)right).getConjuncts();
      for (PsiType conjunct : conjuncts) {
        if (isAssignable(left, conjunct, allowUncheckedConversion)) return true;
      }
      return false;
    }

    if (left instanceof PsiCapturedWildcardType) {
      return left.equals(right) || isAssignable(((PsiCapturedWildcardType)left).getLowerBound(), right, allowUncheckedConversion);
    } else if (right instanceof PsiCapturedWildcardType) {
      return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion);
    }

    if (left instanceof PsiWildcardType) {
      return isAssignableToWildcard((PsiWildcardType)left, right);
    }
    else if (right instanceof PsiWildcardType) {
      return isAssignableFromWildcard(left, (PsiWildcardType)right);
    }
    if (right instanceof PsiArrayType) {
      if (!(left instanceof PsiArrayType)) {
        if (left instanceof PsiPrimitiveType || PsiUtil.resolveClassInType(left) == null) return false;
        PsiClass lClass = PsiUtil.resolveClassInType(left);
        if (lClass == null) return false;
        if (lClass.isInterface()) {
          final String qualifiedName = lClass.getQualifiedName();
          return "java.io.Serializable".equals(qualifiedName) || "java.lang.Cloneable".equals(qualifiedName);
        }
        else {
          return left.equalsToText("java.lang.Object");
        }
      }
      PsiType lCompType = ((PsiArrayType)left).getComponentType();
      PsiType rCompType = ((PsiArrayType)right).getComponentType();
      if (lCompType instanceof PsiPrimitiveType) {
        return lCompType == rCompType;
      }
      else {
        return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion);
      }
    }
    else {
      if (left instanceof PsiArrayType) return false;
      if (right instanceof PsiPrimitiveType) {
        if (!(left instanceof PsiPrimitiveType)) {
          return left instanceof PsiClassType && isBoxable((PsiClassType)left, (PsiPrimitiveType)right);
        }
        int leftTypeIndex = TYPE_TO_RANK_MAP.get(left) - 1;
        if (leftTypeIndex < 0) return false;
        int rightTypeIndex = TYPE_TO_RANK_MAP.get(right) - 1;
        if (rightTypeIndex < 0) return false;
        return IS_ASSIGNABLE_BIT_SET[rightTypeIndex][leftTypeIndex];
      }
      else {
        if (!(right instanceof PsiClassType)) {
          LOG.error(right.toString());
        }
        if (left instanceof PsiPrimitiveType) {
          return isUnboxable((PsiPrimitiveType)left, (PsiClassType)right);
        }
        final PsiClassType.ClassResolveResult leftResult = PsiUtil.resolveGenericsClassInType(left);
        final PsiClassType.ClassResolveResult rightResult = PsiUtil.resolveGenericsClassInType(right);
        if (leftResult.getElement() == null || rightResult.getElement() == null) {
          if (leftResult.getElement() != rightResult.getElement()) return false;
          // let's suppose 2 unknown classes, which could be the same to be the same
          String lText = left.getPresentableText();
          String rText = right.getPresentableText();
          if (lText.equals(rText)) return true;
          if (lText.length() > rText.length() && lText.endsWith(rText) &&
              lText.charAt(lText.length() - rText.length() - 1) == '.') {
            return true;
          }
          return rText.length() > lText.length()
                 && rText.endsWith(lText)
                 && rText.charAt(rText.length() - lText.length() - 1) == '.';
        }
        return isClassAssignable(leftResult, rightResult, allowUncheckedConversion);
      }
    }
  }

  private static boolean isAssignableFromWildcard(PsiType left, PsiWildcardType rightWildcardType) {
    return isAssignable(left, rightWildcardType.getExtendsBound());
  }

  private static boolean isAssignableToWildcard(PsiWildcardType wildcardType, PsiType right) {
    if (wildcardType.isSuper()) {
      return isAssignable(right, wildcardType.getSuperBound());
    }
    return isAssignable(wildcardType.getExtendsBound(), right);
  }

  private static boolean isUnboxable(final PsiPrimitiveType left, final PsiClassType right) {
    final PsiPrimitiveType rightUnboxedType = PsiPrimitiveType.getUnboxedType(right);
    return rightUnboxedType != null && isAssignable(left, rightUnboxedType);
  }

  public static boolean boxingConversionApplicable(final PsiType left, final PsiType right) {
    if (left instanceof PsiPrimitiveType && !PsiType.NULL.equals(left)) {
      return right instanceof PsiClassType && isAssignable(left, right);
    }
    else {
      return left instanceof PsiClassType
                && right instanceof PsiPrimitiveType
                && !PsiType.NULL.equals(right)
                && isAssignable(left, right);
    }
  }

  private static boolean isBoxable(final PsiClassType left, final PsiPrimitiveType right) {
    if (!left.getLanguageLevel().hasEnumKeywordAndAutoboxing()) return false;
    final PsiClass psiClass = left.resolve();
    if (psiClass == null) return false;
    final PsiClassType rightBoxed = right.getBoxedType(psiClass.getManager(), left.getResolveScope());
    return rightBoxed != null && isAssignable(left, rightBoxed);
  }

  private static boolean isClassAssignable(PsiClassType.ClassResolveResult leftResult,
                                           PsiClassType.ClassResolveResult rightResult,
                                           boolean allowUncheckedConversion) {
    final PsiClass leftClass = leftResult.getElement();
    final PsiClass rightClass = rightResult.getElement();
    return leftClass != null
           && rightClass != null
           && InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true)
           && typeParametersAgree(leftResult, rightResult, allowUncheckedConversion);
  }

  private static boolean typeParametersAgree(PsiClassType.ClassResolveResult leftResult,
                                             PsiClassType.ClassResolveResult rightResult,
                                             boolean allowUncheckedConversion) {
    PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor();
    PsiClass leftClass = leftResult.getElement();
    PsiClass rightClass = rightResult.getElement();

    if (!leftClass.hasTypeParameters()) return true;
    PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor();

    if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) {
      rightSubstitutor = getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor);
      rightClass = leftClass;
    }
    else if (!rightClass.hasTypeParameters()) return true;

    Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass);
    Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass);
    while (li.hasNext()) {
      if (!ri.hasNext()) return false;
      PsiTypeParameter lp = li.next();
      PsiTypeParameter rp = ri.next();
      final PsiType typeLeft = leftSubstitutor.substitute(lp);
      if (typeLeft == null) continue;
      final PsiType typeRight = rightSubstitutor.substituteWithBoundsPromotion(rp);
      if (typeRight == null) {
        // compatibility feature: allow to assign raw types to generic ones
        return allowUncheckedConversion;
      }
      if (!typesAgree(typeLeft, typeRight)) return false;
    }
    return true;
  }

  private static boolean typesAgree(PsiType typeLeft, PsiType typeRight) {
    if (typeLeft instanceof PsiWildcardType) {
      final PsiWildcardType leftWildcard = (PsiWildcardType)typeLeft;
      final PsiType leftBound = leftWildcard.getBound();
      if (leftBound == null) return true;
      if (leftBound.equalsToText("java.lang.Object")) {
        if (!leftWildcard.isSuper()) return true;
        if (typeRight.equalsToText("java.lang.Object")) return true;
      }

      if (typeRight instanceof PsiWildcardType) {
        final PsiWildcardType rightWildcard = (PsiWildcardType)typeRight;
        if (leftWildcard.isExtends()) {
          return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), false);
        }
        else { //isSuper
          return rightWildcard.isSuper() && isAssignable(rightWildcard.getBound(), leftBound, false);
        }
      }
      else {
        if (leftWildcard.isExtends()) {
          return isAssignable(leftBound, typeRight, false);
        }
        else { // isSuper
          return isAssignable(typeRight, leftBound, false);
        }
      }
    }
    else {
      return typeLeft.equals(typeRight);
    }
  }

  @Nullable
  public static PsiSubstitutor getClassSubstitutor(PsiClass superClassCandidate,
                                                   PsiClass derivedClassCandidate,
                                                   PsiSubstitutor derivedSubstitutor) {
    if (superClassCandidate.getManager().areElementsEquivalent(superClassCandidate, derivedClassCandidate)) {
      PsiTypeParameter[] baseParams = superClassCandidate.getTypeParameters();
      PsiTypeParameter[] derivedParams = derivedClassCandidate.getTypeParameters();
      if (baseParams.length > 0 && derivedParams.length == 0) {
        return superClassCandidate.getManager().getElementFactory().createRawSubstitutor(superClassCandidate);
      }
      return derivedSubstitutor;
    }
    if (!derivedClassCandidate.isInheritor(superClassCandidate, true)) return null;
    return getSuperClassSubstitutor(superClassCandidate, derivedClassCandidate, derivedSubstitutor);
  }

  /**
   * Calculates substitutor that binds type parameters in <code>superClass</code> with
   * values that they have in <code>derivedClass</code>, given that type parameters in
   * <code>derivedClass</code> are bound by <code>derivedSubstitutor</code>.
   * <code>superClass</code> must be a super class/interface of <code>derivedClass</code> (as in
   * <code>InheritanceUtil.isInheritor(derivedClass, superClass, true)</code>
   *
   * @param superClass
   * @param derivedClass
   * @param derivedSubstitutor
   * @return substitutor (never returns <code>null</code>)
   * @see InheritanceUtil#isInheritor(PsiClass, PsiClass, boolean)
   */
  @NotNull
  public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass,
                                                        PsiClass derivedClass,
                                                        PsiSubstitutor derivedSubstitutor) {
    // [dsl] assertion commented out since we no longer cache isInheritor
    //LOG.assertTrue(derivedClass.isInheritor(superClass, true), "Not inheritor: " + derivedClass + " super: " + superClass);

    if (!superClass.hasTypeParameters() && superClass.getContainingClass() == null) return PsiSubstitutor.EMPTY; //optimization

    final PsiManager manager = superClass.getManager();
    if (PsiUtil.isRawSubstitutor(derivedClass, derivedSubstitutor)) {
      return manager.getElementFactory().createRawSubstitutor(superClass);
    }

    final PsiClass objectClass = manager.findClass("java.lang.Object", superClass.getResolveScope());
    if (manager.areElementsEquivalent(superClass, objectClass)) {
      return PsiSubstitutor.EMPTY;
    }

    PsiSubstitutor substitutor;
    final Set<PsiClass> visited = new THashSet<PsiClass>();
    if (derivedClass instanceof PsiAnonymousClass) {
      final PsiClassType baseType = ((PsiAnonymousClass)derivedClass).getBaseClassType();
      final JavaResolveResult result = baseType.resolveGenerics();
      if (result.getElement() == null) return PsiSubstitutor.UNKNOWN;
      substitutor = getSuperClassSubstitutorInner(superClass, (PsiClass)result.getElement(),
                                                  derivedSubstitutor.putAll(result.getSubstitutor()), visited, manager);
    }
    else {
      substitutor = getSuperClassSubstitutorInner(superClass, derivedClass, derivedSubstitutor, visited, manager);
    }
    if (substitutor == null) {
      LOG.error("Not inheritor: " + derivedClass + " super: " + superClass);
    }
    return substitutor;
  }

  private static PsiSubstitutor getSuperClassSubstitutorInner(PsiClass base,
                                                              PsiClass candidate,
                                                              PsiSubstitutor candidateSubstitutor,
                                                              Set<PsiClass> visited,
                                                              PsiManager manager) {
    if (visited.contains(candidate)) return null;
    visited.add(candidate);

    if (base == candidate) return candidateSubstitutor;
    if (manager.areElementsEquivalent(base, candidate)) {
      PsiTypeParameter[] baseParams = base.getTypeParameters();
      PsiTypeParameter[] candidateParams = candidate.getTypeParameters();
      PsiElementFactory factory = base.getManager().getElementFactory();
      if (baseParams.length > 0 && candidateParams.length == 0) {
        return factory.createRawSubstitutor(base);
      }
      else {
        Map<PsiTypeParameter, PsiType> m = new HashMap<PsiTypeParameter, PsiType>();
        for (int i = 0; i < candidateParams.length && i < baseParams.length; i++) {
          m.put(baseParams[i], candidateSubstitutor.substitute(candidateParams[i]));
        }
        return factory.createSubstitutor(m);
      }
    }

    PsiSubstitutor substitutor = checkReferenceList(candidate.getExtendsListTypes(), candidateSubstitutor, base, visited,
                                                    manager);
    if (substitutor == null) {
      substitutor = checkReferenceList(candidate.getImplementsListTypes(), candidateSubstitutor, base, visited, manager);
    }
    return substitutor;
  }

  private static PsiSubstitutor checkReferenceList(final PsiClassType[] types, PsiSubstitutor candidateSubstitutor,
                                                   PsiClass base,
                                                   Set<PsiClass> set,
                                                   PsiManager manager) {
    for (final PsiClassType type : types) {
      final PsiType substitutedType = candidateSubstitutor.substitute(type);
      //if (!(substitutedType instanceof PsiClassType)) return null;
      LOG.assertTrue(substitutedType instanceof PsiClassType);

      final JavaResolveResult result = ((PsiClassType)substitutedType).resolveGenerics();
      final PsiElement newCandidate = result.getElement();
      if (newCandidate != null) {
        final PsiSubstitutor substitutor = result.getSubstitutor();
        final PsiSubstitutor newSubstitutor = getSuperClassSubstitutorInner(base, (PsiClass)newCandidate,
                                                                            substitutor, set, manager);
        if (newSubstitutor != null) {
          return type.isRaw() ? manager.getElementFactory().createRawSubstitutor(base) : newSubstitutor;
        }
      }
    }
    return null;
  }

  /**
   * see JLS 5.6.2
   */
  public static PsiType binaryNumericPromotion(PsiType type1, PsiType type2) {
    if (isDoubleType(type1)) return unbox(type1);
    if (isDoubleType(type2)) return unbox(type2);
    if (isFloatType(type1)) return unbox(type1);
    if (isFloatType(type2)) return unbox(type2);
    if (isLongType(type1)) return unbox(type1);
    if (isLongType(type2)) return unbox(type2);

    return PsiType.INT;
  }

  private static PsiType unbox(PsiType type) {
    if (type instanceof PsiPrimitiveType) return type;
    if (type instanceof PsiClassType) {
      type = PsiPrimitiveType.getUnboxedType(type);
      LOG.assertTrue(type != null);
      return type;
    }
    LOG.error("Invalid type for unboxing "+type);
    return type;
  }

  private static final Set<String> INTEGER_NUMBER_TYPES = new THashSet<String>(5);

  static {
    INTEGER_NUMBER_TYPES.add(PsiType.BYTE.getCanonicalText());
    INTEGER_NUMBER_TYPES.add(PsiType.CHAR.getCanonicalText());
    INTEGER_NUMBER_TYPES.add(PsiType.LONG.getCanonicalText());
    INTEGER_NUMBER_TYPES.add(PsiType.INT.getCanonicalText());
    INTEGER_NUMBER_TYPES.add(PsiType.SHORT.getCanonicalText());
  }

  private static final Set<String> PRIMITIVE_TYPES = new THashSet<String>(9);

  static {
    PRIMITIVE_TYPES.add(PsiType.VOID.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.BYTE.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.CHAR.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.DOUBLE.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.FLOAT.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.LONG.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.INT.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.SHORT.getCanonicalText());
    PRIMITIVE_TYPES.add(PsiType.BOOLEAN.getCanonicalText());
  }

  private static final Set<String> PRIMITIVE_WRAPPER_TYPES = new THashSet<String>(8);

  static {
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Byte");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Character");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Double");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Float");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Long");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Integer");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Short");
    PRIMITIVE_WRAPPER_TYPES.add("java.lang.Boolean");
  }

  public static boolean isIntegerNumber(String typeName) {
    return INTEGER_NUMBER_TYPES.contains(typeName);
  }

  public static boolean isPrimitive(String typeName) {
    return PRIMITIVE_TYPES.contains(typeName);
  }

  public static boolean isPrimitiveWrapper(String typeName) {
    return PRIMITIVE_WRAPPER_TYPES.contains(typeName);
  }
  public static boolean isPrimitiveWrapper(final PsiType type) {
    return type != null && isPrimitiveWrapper(type.getCanonicalText());
  }

  public static PsiType typeParameterErasure(final PsiTypeParameter typeParameter) {
    return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY);
  }

  private static PsiType typeParameterErasure(final PsiTypeParameter typeParameter, final PsiSubstitutor beforeSubstitutor) {
    final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
    if (extendsList.length > 0) {
      final PsiClass psiClass = extendsList[0].resolve();
      if (psiClass instanceof PsiTypeParameter) {
        Set<PsiClass> visited = new THashSet<PsiClass>();
        visited.add(psiClass);
        final PsiTypeParameter boundTypeParameter = (PsiTypeParameter)psiClass;
        if (beforeSubstitutor.getSubstitutionMap().containsKey(boundTypeParameter)) {
          return erasure(beforeSubstitutor.substitute(boundTypeParameter));
        }
        return typeParameterErasureInner(boundTypeParameter, visited);
      }
      else if (psiClass != null) {
        return typeParameter.getManager().getElementFactory().createType(psiClass);
      }
    }
    return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
  }

  private static PsiClassType typeParameterErasureInner(PsiTypeParameter typeParameter, Set<PsiClass> visited) {
    final PsiClassType[] extendsList = typeParameter.getExtendsList().getReferencedTypes();
    if (extendsList.length > 0) {
      final PsiClass psiClass = extendsList[0].resolve();
      if (psiClass instanceof PsiTypeParameter) {
        if (!visited.contains(psiClass)) {
          visited.add(psiClass);
          return typeParameterErasureInner((PsiTypeParameter)psiClass, visited);
        }
      }
      else if (psiClass != null) {
        return typeParameter.getManager().getElementFactory().createType(psiClass);
      }
    }
    return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope());
  }

  public static PsiType erasure(PsiType type) {
    return erasure(type, PsiSubstitutor.EMPTY);
  }

  public static PsiType erasure(PsiType type, final PsiSubstitutor beforeSubstitutor) {
    if (type == null) return null;
    return type.accept(new PsiTypeVisitor<PsiType>() {
      public PsiType visitClassType(PsiClassType classType) {
        final PsiClass aClass = classType.resolve();
        if (!(aClass instanceof PsiTypeParameter)) {
          return classType.rawType();
        }
        else {
          return typeParameterErasure((PsiTypeParameter)aClass, beforeSubstitutor);
        }
      }

      public PsiType visitWildcardType(PsiWildcardType wildcardType) {
        return wildcardType.getExtendsBound().accept(this);
      }

      public PsiType visitPrimitiveType(PsiPrimitiveType primitiveType) {
        return primitiveType;
      }

      public PsiType visitEllipsisType(PsiEllipsisType ellipsisType) {
        final PsiType componentType = ellipsisType.getComponentType();
        final PsiType newComponentType = componentType.accept(this);
        if (newComponentType == componentType) return ellipsisType;
        return new PsiArrayType(newComponentType);
      }

      public PsiType visitArrayType(PsiArrayType arrayType) {
        final PsiType componentType = arrayType.getComponentType();
        final PsiType newComponentType = componentType.accept(this);
        if (newComponentType == componentType) return arrayType;
        return newComponentType.createArrayType();
      }
    });
  }

  public static Object computeCastTo(final Object operand, final PsiType castType) {
    if (operand == null || castType == null) return null;
    Object value;
    if (operand instanceof String && castType.equalsToText("java.lang.String")) {
      value = operand;
    }
    else if (operand instanceof Boolean && castType == PsiType.BOOLEAN) {
      value = operand;
    }
    else {
      final PsiType primitiveType = wrapperToPrimitive(operand);
      if (primitiveType == null) return null;
      // identity cast, including (boolean)boolValue
      if (castType.equals(primitiveType)) return operand;
      final int rankFrom = getTypeRank(primitiveType);
      if (rankFrom > caster.length) return null;
      final int rankTo = getTypeRank(castType);
      if (rankTo > caster.length) return null;

      value = caster[rankFrom - 1][rankTo - 1].cast(operand);
    }
    return value;
  }

  private interface Caster {
    Object cast(Object operand);
  }

  private static final Caster[][] caster = new Caster[][]{
    {
      new Caster() {
        public Object cast(Object operand) {
          return operand;
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf((short)((Number)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character((char) ((Number) operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf(((Number)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf(((Number)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Number) operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Number) operand).intValue());
        }
      }
    }
    ,
    {
      new Caster() {
        public Object cast(Object operand) {
          return Byte.valueOf((byte)((Short)operand).shortValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf(((Short)operand).shortValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character((char) ((Short) operand).shortValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf(((Short)operand).shortValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf(((Short)operand).shortValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Short) operand).shortValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Short) operand).shortValue());
        }
      }
    }
    ,
    {
      new Caster() {
        public Object cast(Object operand) {
          return Byte.valueOf((byte)((Character)operand).charValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf((short)((Character)operand).charValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character(((Character) operand).charValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf(((Character)operand).charValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf(((Character)operand).charValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Character) operand).charValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Character) operand).charValue());
        }
      }
    }
    ,
    {
      new Caster() {
        public Object cast(Object operand) {
          return Byte.valueOf((byte)((Integer)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf((short)((Integer)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character((char) ((Integer) operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf(((Integer)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf(((Integer)operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Integer) operand).intValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Integer) operand).intValue());
        }
      }
    }
    ,
    {
      new Caster() {
        public Object cast(Object operand) {
          return Byte.valueOf((byte)((Long)operand).longValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf((short)((Long)operand).longValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character((char) ((Long) operand).longValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf((int)((Long)operand).longValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf(((Long)operand).longValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Long) operand).longValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Long) operand).longValue());
        }
      }
    }
    ,
    {
      new Caster() {
        public Object cast(Object operand) {
          return Byte.valueOf((byte)((Float)operand).floatValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf((short)((Float)operand).floatValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character((char) ((Float) operand).floatValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf((int)((Float)operand).floatValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf((long)((Float)operand).floatValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Float) operand).floatValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Float) operand).floatValue());
        }
      }
    }
    ,
    {
      new Caster() {
        public Object cast(Object operand) {
          return Byte.valueOf((byte)((Double)operand).doubleValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Short.valueOf((short)((Double)operand).doubleValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Character((char) ((Double) operand).doubleValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Integer.valueOf((int)((Double)operand).doubleValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return Long.valueOf((long)((Double)operand).doubleValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Float(((Double) operand).doubleValue());
        }
      }
      , new Caster() {
        public Object cast(Object operand) {
          return new Double(((Double) operand).doubleValue());
        }
      }
    }
  };

  private static final Map<Class, PsiType> WRAPPER_TO_PRIMITIVE = new THashMap<Class, PsiType>(8);
  static {
    WRAPPER_TO_PRIMITIVE.put(Boolean.class, PsiType.BOOLEAN);
    WRAPPER_TO_PRIMITIVE.put(Byte.class, PsiType.BYTE);
    WRAPPER_TO_PRIMITIVE.put(Character.class, PsiType.CHAR);
    WRAPPER_TO_PRIMITIVE.put(Short.class, PsiType.SHORT);
    WRAPPER_TO_PRIMITIVE.put(Integer.class, PsiType.INT);
    WRAPPER_TO_PRIMITIVE.put(Long.class, PsiType.LONG);
    WRAPPER_TO_PRIMITIVE.put(Float.class, PsiType.FLOAT);
    WRAPPER_TO_PRIMITIVE.put(Double.class, PsiType.DOUBLE);
  }

  private static PsiType wrapperToPrimitive(Object o) {
    return WRAPPER_TO_PRIMITIVE.get(o.getClass());
  }
}
TOP

Related Classes of com.intellij.psi.util.TypeConversionUtil$Caster

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.