| /* |
| * Copyright 2000-2014 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.openapi.project.Project; |
| import com.intellij.openapi.roots.ProjectRootModificationTracker; |
| import com.intellij.openapi.util.*; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.*; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.util.Processor; |
| import com.intellij.util.containers.ConcurrentHashSet; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.HashMap; |
| import gnu.trove.THashMap; |
| import gnu.trove.THashSet; |
| import gnu.trove.TObjectIntHashMap; |
| import org.jetbrains.annotations.Contract; |
| import org.jetbrains.annotations.NonNls; |
| 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; |
| |
| import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING; |
| |
| public class TypeConversionUtil { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.TypeConversionUtil"); |
| |
| private static final boolean[][] IS_ASSIGNABLE_BIT_SET = { |
| {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>(); |
| |
| 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; |
| public static final int LONG_RANK = 5; |
| private static final int FLOAT_RANK = 6; |
| private static final int DOUBLE_RANK = 7; |
| private static final int BOOL_RANK = 10; |
| private static final int STRING_RANK = 100; |
| private static final int MAX_NUMERIC_RANK = DOUBLE_RANK; |
| public static final PsiType NULL_TYPE = new PsiEllipsisType(PsiType.NULL){ |
| @Override |
| public boolean isValid() { |
| return true; |
| } |
| |
| @NotNull |
| @Override |
| @NonNls |
| public String getPresentableText() { |
| return "FAKE TYPE"; |
| } |
| }; |
| |
| 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); |
| TYPE_TO_RANK_MAP.put(PsiType.BOOLEAN, BOOL_RANK); |
| } |
| |
| private TypeConversionUtil() { } |
| |
| /** |
| * @return true if fromType can be casted to toType |
| */ |
| public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType) { |
| return areTypesConvertible(fromType, toType, null); |
| } |
| |
| /** |
| * @return true if fromType can be casted to toType |
| */ |
| public static boolean areTypesConvertible(@NotNull PsiType fromType, @NotNull PsiType toType, @Nullable LanguageLevel languageLevel) { |
| 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; |
| if (toType instanceof PsiIntersectionType) { |
| for (PsiType type : ((PsiIntersectionType)toType).getConjuncts()) { |
| if (!areTypesConvertible(fromType, type)) return false; |
| } |
| 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 || toClass instanceof PsiTypeParameter) return false; |
| PsiClassType boxedType = ((PsiPrimitiveType)fromType).getBoxedType(toClass.getManager(), toType.getResolveScope()); |
| return boxedType != null && areTypesConvertible(boxedType, toType); |
| } |
| if (!fromIsPrimitive) { |
| // 5.5. Casting Contexts |
| if ((fromTypeRank == SHORT_RANK || fromTypeRank == BYTE_RANK) && toTypeRank == CHAR_RANK) return false; |
| |
| if (fromType instanceof PsiClassType) { |
| if (languageLevel == null) { |
| languageLevel = ((PsiClassType)fromType).getLanguageLevel(); |
| } |
| |
| if (languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) { |
| final PsiClassType classType = (PsiClassType)fromType; |
| final PsiClass psiClass = classType.resolve(); |
| if (psiClass == null || psiClass instanceof PsiTypeParameter) return false; |
| final PsiClassType boxedType = ((PsiPrimitiveType)toType).getBoxedType(psiClass.getManager(), psiClass.getResolveScope()); |
| if (boxedType != null && isAssignable(fromType, boxedType)) { |
| return true; |
| } |
| } |
| } |
| return fromTypeRank == toTypeRank || |
| 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.1.6 |
| */ |
| private static boolean isNarrowingReferenceConversionAllowed(@NotNull PsiType fromType, @NotNull PsiType toType) { |
| if (toType instanceof PsiPrimitiveType || fromType instanceof PsiPrimitiveType) return fromType.equals(toType); |
| //Done with primitives |
| if (toType instanceof PsiDiamondType || fromType instanceof PsiDiamondType) return false; |
| 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 (!areTypesConvertible(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) { |
| if (fromType instanceof PsiClassType && ((PsiClassType)fromType).getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_8)) { |
| for (PsiType conjunct : ((PsiIntersectionType)toType).getConjuncts()) { |
| if (!isNarrowingReferenceConversionAllowed(fromType, conjunct)) return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| if (fromType instanceof PsiDisjunctionType) { |
| return isNarrowingReferenceConversionAllowed(((PsiDisjunctionType)fromType).getLeastUpperBound(), toType); |
| } |
| if (toType instanceof PsiDisjunctionType) { |
| 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(fromType, ((PsiCapturedWildcardType)toType).getWildcard()); |
| } |
| if (fromType instanceof PsiCapturedWildcardType) { |
| return isNarrowingReferenceConversionAllowed(((PsiCapturedWildcardType)fromType).getWildcard(), toType); |
| } |
| |
| if (isAssignable(fromType, toType)) return true; |
| |
| if (!(fromType instanceof PsiClassType) || !(toType instanceof PsiClassType)) return false; |
| 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) || fromClass.isInheritor(toClass, true))&& |
| checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel); |
| } |
| else { |
| if (manager.areElementsEquivalent(fromClass, toClass)) { |
| return areSameParameterTypes(fromClassType, toClassType); |
| } |
| |
| if (toClass.isInheritor(fromClass, true)) { |
| return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel); |
| } |
| else if (fromClass.isInheritor(toClass, true)) { |
| return checkSuperTypesWithDifferentTypeArguments(toResult, fromClass, manager, fromResult.getSubstitutor(), null, languageLevel); |
| } |
| |
| return false; |
| } |
| } |
| else if (!toClass.isInterface()) { |
| if (!toClass.hasModifierProperty(PsiModifier.FINAL)) { |
| return checkSuperTypesWithDifferentTypeArguments(fromResult, toClass, manager, toResult.getSubstitutor(), null, languageLevel); |
| } |
| else { |
| PsiSubstitutor toSubstitutor = getMaybeSuperClassSubstitutor(fromClass, toClass, toResult.getSubstitutor(), null); |
| return toSubstitutor != null && areSameArgumentTypes(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 |
| PsiClassType.ClassResolveResult baseResult; |
| PsiClass derived; |
| PsiSubstitutor derivedSubstitutor; |
| if (toClass.isInheritor(fromClass, true)) { |
| baseResult = fromResult; |
| derived = toClass; |
| derivedSubstitutor = toResult.getSubstitutor(); |
| } |
| else { |
| baseResult = toResult; |
| derived = fromClass; |
| derivedSubstitutor = fromResult.getSubstitutor(); |
| } |
| return checkSuperTypesWithDifferentTypeArguments(baseResult, derived, manager, derivedSubstitutor, null, languageLevel); |
| } |
| } |
| |
| @NotNull |
| private static PsiClassType obtainSafeSuperType(@NotNull 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 JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(superClass, substitutor); |
| } |
| return superType; |
| } |
| |
| private static boolean checkSuperTypesWithDifferentTypeArguments(@NotNull PsiClassType.ClassResolveResult baseResult, |
| @NotNull PsiClass derived, |
| @NotNull PsiManager manager, |
| @NotNull PsiSubstitutor derivedSubstitutor, |
| Set<PsiClass> visited, |
| @NotNull LanguageLevel languageLevel) { |
| if (visited != null && visited.contains(derived)) return true; |
| |
| 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 areSameArgumentTypes(derived, baseResult.getSubstitutor(), derivedSubstitutor, 1); |
| } |
| else { |
| PsiSubstitutor baseSubstitutor = getMaybeSuperClassSubstitutor(derived, base, baseResult.getSubstitutor(), null); |
| if (baseSubstitutor != null) { |
| derivedSubstitutor = getSuperClassSubstitutor(derived, derived, derivedSubstitutor); |
| if (!areSameArgumentTypes(derived, baseSubstitutor, derivedSubstitutor)) return false; |
| } |
| } |
| |
| if (visited == null) visited = new THashSet<PsiClass>(); |
| visited.add(derived); |
| 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 areSameParameterTypes(@NotNull PsiClassType type1, @NotNull PsiClassType type2) { |
| PsiClassType.ClassResolveResult resolveResult1 = type1.resolveGenerics(); |
| PsiClassType.ClassResolveResult resolveResult2 = type2.resolveGenerics(); |
| final PsiClass aClass = resolveResult1.getElement(); |
| final PsiClass bClass = resolveResult2.getElement(); |
| return aClass != null && |
| bClass != null && |
| aClass.getManager().areElementsEquivalent(aClass, bClass) && |
| areSameArgumentTypes(aClass, resolveResult1.getSubstitutor(), resolveResult2.getSubstitutor()); |
| } |
| |
| private static boolean areSameArgumentTypes(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor1, @NotNull PsiSubstitutor substitutor2) { |
| return areSameArgumentTypes(aClass, substitutor1, substitutor2, 0); |
| } |
| |
| private static boolean areSameArgumentTypes(@NotNull PsiClass aClass, |
| @NotNull PsiSubstitutor substitutor1, |
| @NotNull PsiSubstitutor substitutor2, |
| int level) { |
| for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) { |
| PsiType typeArg1 = substitutor1.substitute(typeParameter); |
| PsiType typeArg2 = substitutor2.substitute(typeParameter); |
| if (typeArg1 == null || typeArg2 == null) return true; |
| if (TypesDistinctProver.provablyDistinct(typeArg1, typeArg2, level)) return false; |
| |
| final PsiClass class1 = PsiUtil.resolveClassInType(typeArg1); |
| if (class1 instanceof PsiTypeParameter) { |
| for (PsiType type : class1.getExtendsListTypes()) { |
| if (TypesDistinctProver.provablyDistinct(type, typeArg2) && !isAssignable(type, typeArg2)) return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| 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.equals(type); |
| } |
| |
| public static boolean isFloatOrDoubleType(PsiType type) { |
| return isFloatType(type) || isDoubleType(type); |
| } |
| public static boolean isDoubleType(PsiType type) { |
| return PsiType.DOUBLE.equals(type) || PsiType.DOUBLE.equals(PsiPrimitiveType.getUnboxedType(type)); |
| } |
| |
| public static boolean isFloatType(PsiType type) { |
| return PsiType.FLOAT.equals(type) || PsiType.FLOAT.equals(PsiPrimitiveType.getUnboxedType(type)); |
| } |
| |
| public static boolean isLongType(PsiType type) { |
| return PsiType.LONG.equals(type) || PsiType.LONG.equals(PsiPrimitiveType.getUnboxedType(type)); |
| } |
| |
| public static boolean isVoidType(PsiType type) { |
| return PsiType.VOID.equals(type); |
| } |
| |
| public static boolean isBooleanType(PsiType type) { |
| return PsiType.BOOLEAN.equals(type) || PsiType.BOOLEAN.equals(PsiPrimitiveType.getUnboxedType(type)); |
| } |
| |
| public static boolean isNumericType(int typeRank) { |
| return typeRank <= MAX_NUMERIC_RANK; |
| } |
| public static boolean isNumericType(PsiType type) { |
| return type != null && 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 others |
| */ |
| public static int getTypeRank(@NotNull 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 (type.equalsToText(JAVA_LANG_STRING)) return STRING_RANK; |
| return Integer.MAX_VALUE; |
| } |
| |
| /** |
| * @param tokenType JavaTokenType enumeration |
| * @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(); |
| return isBinaryOperatorApplicable(tokenType, ltype, rtype, strict); |
| } |
| |
| public static boolean isBinaryOperatorApplicable(final IElementType tokenType, final PsiType ltype, final PsiType rtype, final boolean strict) { |
| 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)) { |
| if (rtype instanceof PsiClassType) { |
| final LanguageLevel languageLevel = ((PsiClassType)rtype).getLanguageLevel(); |
| if (languageLevel.isAtLeast(LanguageLevel.JDK_1_5) && |
| !languageLevel.isAtLeast(LanguageLevel.JDK_1_8) && |
| areTypesConvertible(ltype, rtype)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| if (isPrimitiveAndNotNull(rtype)) { |
| if (ltype instanceof PsiClassType) { |
| final LanguageLevel level = ((PsiClassType)ltype).getLanguageLevel(); |
| if (level.isAtLeast(LanguageLevel.JDK_1_7) && !level.isAtLeast(LanguageLevel.JDK_1_8) && areTypesConvertible(rtype, ltype)) { |
| return true; |
| } |
| } |
| 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(CommonClassNames.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(@NotNull PsiJavaToken token, PsiExpression operand) { |
| if (operand == null) return false; |
| PsiType type = operand.getType(); |
| return type != null && isUnaryOperatorApplicable(token, type); |
| } |
| |
| public static boolean isUnaryOperatorApplicable(@NotNull PsiJavaToken token, @NotNull PsiType type) { |
| IElementType i = token.getTokenType(); |
| int typeRank = getTypeRank(type); |
| if (i == JavaTokenType.MINUSMINUS || i == JavaTokenType.PLUSPLUS) { |
| return typeRank <= MAX_NUMERIC_RANK; |
| } |
| if (i == JavaTokenType.MINUS || i == JavaTokenType.PLUS) { |
| return typeRank <= MAX_NUMERIC_RANK; |
| } |
| if (i == JavaTokenType.TILDE) { |
| return typeRank <= LONG_RANK; |
| } |
| if (i == JavaTokenType.EXCL) { |
| return typeRank == BOOL_RANK; |
| } |
| 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 = JavaPsiFacade.getInstance(rExpr.getProject()).getConstantEvaluationHelper().computeConstantExpression(rExpr); |
| final long value; |
| if (rValue instanceof Number) { |
| value = ((Number)rValue).longValue(); |
| } |
| else if (rValue instanceof Character) { |
| value = (Character)rValue; |
| } |
| else { |
| return false; |
| } |
| |
| if (PsiType.BYTE.equals(lType)) { |
| return -128 <= value && value <= 127; |
| } |
| else if (PsiType.SHORT.equals(lType)) { |
| return -32768 <= value && value <= 32767; |
| } |
| else if (PsiType.CHAR.equals(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 || left.equals(right)) return true; |
| |
| if (isNullType(right)) { |
| return !(left instanceof PsiPrimitiveType) || isNullType(left); |
| } |
| |
| if (right instanceof PsiMethodReferenceType) { |
| final PsiMethodReferenceExpression methodReferenceExpression = ((PsiMethodReferenceType)right).getExpression(); |
| if (left instanceof PsiLambdaExpressionType) { |
| final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType(); |
| final PsiType lType = ((PsiLambdaExpressionType)left).getExpression().getFunctionalInterfaceType(); |
| return Comparing.equal(rType, lType); |
| } else if (left instanceof PsiMethodReferenceType) { |
| final PsiType rType = methodReferenceExpression.getFunctionalInterfaceType(); |
| final PsiType lType = ((PsiMethodReferenceType)left).getExpression().getFunctionalInterfaceType(); |
| return Comparing.equal(rType, lType); |
| } |
| return !(left instanceof PsiArrayType) && methodReferenceExpression.isAcceptable(left); |
| } |
| if (right instanceof PsiLambdaExpressionType) { |
| final PsiLambdaExpression rLambdaExpression = ((PsiLambdaExpressionType)right).getExpression(); |
| if (left instanceof PsiLambdaExpressionType) { |
| final PsiLambdaExpression lLambdaExpression = ((PsiLambdaExpressionType)left).getExpression(); |
| final PsiType rType = rLambdaExpression.getFunctionalInterfaceType(); |
| final PsiType lType = lLambdaExpression.getFunctionalInterfaceType(); |
| return Comparing.equal(rType, lType); |
| } |
| return !(left instanceof PsiArrayType) && rLambdaExpression.isAcceptable(left, false); |
| } |
| |
| if (left instanceof PsiIntersectionType) { |
| PsiType[] conjuncts = ((PsiIntersectionType)left).getConjuncts(); |
| for (PsiType conjunct : conjuncts) { |
| if (!isAssignable(conjunct, right, allowUncheckedConversion)) return false; |
| } |
| return true; |
| } |
| 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); |
| } |
| if (right instanceof PsiCapturedWildcardType) { |
| return isAssignable(left, ((PsiCapturedWildcardType)right).getUpperBound(), allowUncheckedConversion); |
| } |
| |
| if (left instanceof PsiWildcardType) { |
| return isAssignableToWildcard((PsiWildcardType)left, right); |
| } |
| 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(CommonClassNames.JAVA_LANG_OBJECT); |
| } |
| } |
| PsiType lCompType = ((PsiArrayType)left).getComponentType(); |
| PsiType rCompType = ((PsiArrayType)right).getComponentType(); |
| if (lCompType instanceof PsiPrimitiveType) { |
| return lCompType.equals(rCompType); |
| } |
| return !(rCompType instanceof PsiPrimitiveType) && isAssignable(lCompType, rCompType, allowUncheckedConversion); |
| } |
| |
| if (left instanceof PsiDisjunctionType) { |
| for (PsiType type : ((PsiDisjunctionType)left).getDisjunctions()) { |
| if (isAssignable(type, right, allowUncheckedConversion)) return true; |
| } |
| return false; |
| } |
| if (right instanceof PsiDisjunctionType) { |
| return isAssignable(left, ((PsiDisjunctionType)right).getLeastUpperBound(), allowUncheckedConversion); |
| } |
| |
| if (left instanceof PsiArrayType) return false; |
| if (right instanceof PsiPrimitiveType) { |
| if (isVoidType(right)) return false; |
| if (!(left instanceof PsiPrimitiveType)) { |
| return left instanceof PsiClassType && isBoxable((PsiClassType)left, (PsiPrimitiveType)right); |
| } |
| int leftTypeIndex = TYPE_TO_RANK_MAP.get(left) - 1; |
| int rightTypeIndex = TYPE_TO_RANK_MAP.get(right) - 1; |
| return leftTypeIndex >= 0 && |
| rightTypeIndex >= 0 && |
| rightTypeIndex < IS_ASSIGNABLE_BIT_SET.length && |
| leftTypeIndex < IS_ASSIGNABLE_BIT_SET.length && |
| IS_ASSIGNABLE_BIT_SET[rightTypeIndex][leftTypeIndex]; |
| } |
| if (!(right instanceof PsiClassType)) { |
| return false; // must be TypeCook's PsiTypeVariable |
| } |
| 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(@NotNull PsiType left, @NotNull PsiWildcardType rightWildcardType) { |
| if (rightWildcardType.isSuper()) { |
| final PsiClass aClass = PsiUtil.resolveClassInType(rightWildcardType.getSuperBound()); |
| if (aClass instanceof PsiTypeParameter) { |
| final PsiClassType[] types = aClass.getExtendsListTypes(); |
| for (PsiClassType type : types) { |
| if (isAssignable(left, type)) return true; |
| } |
| } |
| } |
| return isAssignable(left, rightWildcardType.getExtendsBound()); |
| } |
| |
| private static boolean isAssignableToWildcard(@NotNull PsiWildcardType wildcardType, @NotNull PsiType right) { |
| if (wildcardType.isSuper()) { |
| return isAssignable(wildcardType.getSuperBound(), right); |
| } |
| return isAssignable(wildcardType.getExtendsBound(), right); |
| } |
| |
| private static boolean isUnboxable(@NotNull PsiPrimitiveType left, @NotNull 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); |
| } |
| |
| if (left instanceof PsiIntersectionType) { |
| for (PsiType lConjunct : ((PsiIntersectionType)left).getConjuncts()) { |
| if (!boxingConversionApplicable(lConjunct, right)) return false; |
| } |
| return true; |
| } |
| |
| return left instanceof PsiClassType |
| && right instanceof PsiPrimitiveType |
| && !PsiType.NULL.equals(right) |
| && isAssignable(left, right); |
| } |
| |
| private static final Key<CachedValue<Set<String>>> POSSIBLE_BOXED_HOLDER_TYPES = Key.create("Types that may be possibly assigned from primitive ones"); |
| |
| private static boolean isBoxable(@NotNull PsiClassType left, @NotNull PsiPrimitiveType right) { |
| if (!left.getLanguageLevel().isAtLeast(LanguageLevel.JDK_1_5)) return false; |
| final PsiClass psiClass = left.resolve(); |
| if (psiClass == null) return false; |
| |
| final String qname = psiClass.getQualifiedName(); |
| if (qname == null || !getAllBoxedTypeSupers(psiClass).contains(qname)) { |
| return false; |
| } |
| |
| final PsiClassType rightBoxed = right.getBoxedType(psiClass.getManager(), left.getResolveScope()); |
| return rightBoxed != null && isAssignable(left, rightBoxed); |
| } |
| |
| @NotNull |
| private static Set<String> getAllBoxedTypeSupers(@NotNull PsiClass psiClass) { |
| PsiManager manager = psiClass.getManager(); |
| final Project project = psiClass.getProject(); |
| CachedValue<Set<String>> boxedHolderTypes = project.getUserData(POSSIBLE_BOXED_HOLDER_TYPES); |
| if (boxedHolderTypes == null) { |
| project.putUserData(POSSIBLE_BOXED_HOLDER_TYPES, boxedHolderTypes = CachedValuesManager.getManager(manager.getProject()).createCachedValue(new CachedValueProvider<Set<String>>() { |
| @Override |
| public Result<Set<String>> compute() { |
| final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); |
| final Set<String> set = new THashSet<String>(); |
| for (final String qname : PsiPrimitiveType.getAllBoxedTypeNames()) { |
| final PsiClass boxedClass = facade.findClass(qname, GlobalSearchScope.allScope(project)); |
| InheritanceUtil.processSupers(boxedClass, true, new Processor<PsiClass>() { |
| @Override |
| public boolean process(PsiClass psiClass) { |
| ContainerUtil.addIfNotNull(psiClass.getQualifiedName(), set); |
| return true; |
| } |
| }); |
| } |
| return Result.create(set, ProjectRootModificationTracker.getInstance(project)); |
| } |
| }, false)); |
| } |
| |
| return boxedHolderTypes.getValue(); |
| } |
| |
| private static boolean isClassAssignable(@NotNull PsiClassType.ClassResolveResult leftResult, |
| @NotNull 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(@NotNull PsiClassType.ClassResolveResult leftResult, |
| @NotNull PsiClassType.ClassResolveResult rightResult, |
| boolean allowUncheckedConversion) { |
| PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor(); |
| PsiClass leftClass = leftResult.getElement(); |
| PsiClass rightClass = rightResult.getElement(); |
| |
| Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass); |
| |
| if (!li.hasNext()) return true; |
| PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor(); |
| |
| if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) { |
| rightSubstitutor = getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor); |
| rightClass = leftClass; |
| } |
| else if (!PsiUtil.typeParametersIterator(rightClass).hasNext()) return true; |
| |
| 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, allowUncheckedConversion)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static final RecursionGuard ourGuard = RecursionManager.createGuard("isAssignable"); |
| |
| public static boolean typesAgree(@NotNull PsiType typeLeft, @NotNull PsiType typeRight, final boolean allowUncheckedConversion) { |
| if (typeLeft instanceof PsiWildcardType) { |
| final PsiWildcardType leftWildcard = (PsiWildcardType)typeLeft; |
| final PsiType leftBound = leftWildcard.getBound(); |
| if (leftBound == null) return true; |
| if (leftBound.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) { |
| if (!leftWildcard.isSuper()) return true; |
| if (typeRight.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) return true; |
| } |
| |
| if (typeRight instanceof PsiWildcardType) { |
| final PsiWildcardType rightWildcard = (PsiWildcardType)typeRight; |
| if (leftWildcard.isExtends()) { |
| return rightWildcard.isExtends() && isAssignable(leftBound, rightWildcard.getBound(), allowUncheckedConversion); |
| } |
| else { //isSuper |
| if (rightWildcard.isSuper()) { |
| final Boolean assignable = ourGuard.doPreventingRecursion(rightWildcard, true, new NotNullComputable<Boolean>() { |
| @NotNull |
| @Override |
| public Boolean compute() { |
| return isAssignable(rightWildcard.getBound(), leftBound, allowUncheckedConversion); |
| } |
| }); |
| if (assignable != null && assignable) { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| else { |
| boolean effectiveAllowUncheckedConversion = allowUncheckedConversion; |
| if (typeRight instanceof PsiCapturedWildcardType) { |
| effectiveAllowUncheckedConversion = false; |
| final PsiClass psiClass = PsiUtil.resolveClassInType(((PsiCapturedWildcardType)typeRight).getWildcard().getBound()); |
| if (psiClass != null && !psiClass.hasTypeParameters()) { |
| effectiveAllowUncheckedConversion = allowUncheckedConversion; |
| } |
| } |
| if (leftWildcard.isExtends()) { |
| return isAssignable(leftBound, typeRight, effectiveAllowUncheckedConversion && !containsWildcards(leftBound)); |
| } |
| else { // isSuper |
| return isAssignable(typeRight, leftBound, false); |
| } |
| } |
| } |
| else { |
| return typeLeft.equals(typeRight); |
| } |
| } |
| |
| public static boolean containsWildcards(@NotNull PsiType leftBound) { |
| return leftBound.accept(new WildcardDetector()); |
| } |
| |
| @Nullable |
| public static PsiSubstitutor getClassSubstitutor(@NotNull PsiClass superClassCandidate, |
| @NotNull PsiClass derivedClassCandidate, |
| @NotNull PsiSubstitutor derivedSubstitutor) { |
| if (superClassCandidate.getManager().areElementsEquivalent(superClassCandidate, derivedClassCandidate)) { |
| PsiTypeParameter[] baseParams = superClassCandidate.getTypeParameters(); |
| PsiTypeParameter[] derivedParams = derivedClassCandidate.getTypeParameters(); |
| if (baseParams.length > 0 && derivedParams.length == 0) { |
| return JavaPsiFacade.getInstance(superClassCandidate.getProject()).getElementFactory().createRawSubstitutor(superClassCandidate); |
| } |
| return derivedSubstitutor; |
| } |
| return getMaybeSuperClassSubstitutor(superClassCandidate, derivedClassCandidate, derivedSubstitutor, null); |
| } |
| |
| private static final Set<String> ourReportedSuperClassSubstitutorExceptions = new ConcurrentHashSet<String>(); |
| |
| /** |
| * 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.isInheritorOrSelf(derivedClass, superClass, true)</code> |
| * |
| * @return substitutor (never returns <code>null</code>) |
| * @see PsiClass#isInheritor(PsiClass, boolean) |
| * @see InheritanceUtil#isInheritorOrSelf(com.intellij.psi.PsiClass, com.intellij.psi.PsiClass, boolean) |
| */ |
| @NotNull |
| public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, |
| @NotNull PsiClass derivedClass, |
| @NotNull PsiSubstitutor derivedSubstitutor) { |
| if (!superClass.hasTypeParameters() && superClass.getContainingClass() == null) return PsiSubstitutor.EMPTY; //optimization and protection against EJB queer hierarchy |
| |
| Set<PsiClass> visited = new THashSet<PsiClass>(); |
| PsiSubstitutor substitutor = getMaybeSuperClassSubstitutor(superClass, derivedClass, derivedSubstitutor, visited); |
| |
| if (substitutor == null) { |
| if (ourReportedSuperClassSubstitutorExceptions.add(derivedClass.getQualifiedName() + "/" + superClass.getQualifiedName())) { |
| reportHierarchyInconsistency(superClass, derivedClass, visited); |
| } |
| return PsiSubstitutor.EMPTY; |
| } |
| return substitutor; |
| } |
| |
| // the same as getSuperClassSubstitutor() but can return null, which means that classes were not inheritors |
| @Nullable |
| public static PsiSubstitutor getMaybeSuperClassSubstitutor(@NotNull PsiClass superClass, |
| @NotNull PsiClass derivedClass, |
| @NotNull PsiSubstitutor derivedSubstitutor, |
| @Nullable Set<PsiClass> visited) { |
| if (!superClass.hasTypeParameters() && superClass.getContainingClass() == null) { |
| return InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true) ? PsiSubstitutor.EMPTY : null; //optimization |
| } |
| |
| final PsiManager manager = superClass.getManager(); |
| if (PsiUtil.isRawSubstitutor(derivedClass, derivedSubstitutor)) { |
| return InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true) ? JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createRawSubstitutor(superClass) : null; |
| } |
| |
| if (CommonClassNames.JAVA_LANG_OBJECT_SHORT.equals(superClass.getName()) && |
| manager.areElementsEquivalent(superClass, JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_LANG_OBJECT, superClass.getResolveScope()))) { |
| return PsiSubstitutor.EMPTY; |
| } |
| |
| if (derivedClass instanceof PsiAnonymousClass) { |
| final PsiClassType baseType = ((PsiAnonymousClass)derivedClass).getBaseClassType(); |
| final JavaResolveResult result = baseType.resolveGenerics(); |
| if (result.getElement() == null) return PsiSubstitutor.UNKNOWN; |
| derivedClass = (PsiClass)result.getElement(); |
| derivedSubstitutor = derivedSubstitutor.putAll(result.getSubstitutor()); |
| } |
| return getSuperClassSubstitutorInner(superClass, derivedClass, derivedSubstitutor, visited == null ? new THashSet<PsiClass>() : visited, manager); |
| } |
| |
| private static void reportHierarchyInconsistency(@NotNull PsiClass superClass, @NotNull PsiClass derivedClass, @NotNull Set<PsiClass> visited) { |
| final StringBuilder msg = new StringBuilder("Super: " + classInfo(superClass)); |
| msg.append("visited:\n"); |
| for (PsiClass aClass : visited) { |
| msg.append(" each: " + classInfo(aClass)); |
| } |
| msg.append("isInheritor: " + InheritanceUtil.isInheritorOrSelf(derivedClass, superClass, true) + " " + derivedClass.isInheritor(superClass, true)); |
| msg.append("\nhierarchy:\n"); |
| InheritanceUtil.processSupers(derivedClass, true, new Processor<PsiClass>() { |
| @Override |
| public boolean process(PsiClass psiClass) { |
| msg.append("each: " + classInfo(psiClass)); |
| return true; |
| } |
| }); |
| LOG.error(msg.toString()); |
| } |
| |
| @NotNull |
| private static String classInfo(@NotNull PsiClass aClass) { |
| String s = aClass.getQualifiedName() + "(" + aClass.getClass().getName() + "; " + PsiUtilCore.getVirtualFile(aClass) + ");\n"; |
| s += "extends: "; |
| for (PsiClassType type : aClass.getExtendsListTypes()) { |
| s += type + " (" + type.getClass().getName() + "; " + type.resolve() + ") "; |
| } |
| s += "\nimplements: "; |
| for (PsiClassType type : aClass.getImplementsListTypes()) { |
| s += type + " (" + type.getClass().getName() + "; " + type.resolve() + ") "; |
| } |
| return s + "\n"; |
| } |
| |
| @NotNull |
| public static PsiSubstitutor getSuperClassSubstitutor(@NotNull PsiClass superClass, @NotNull PsiClassType classType) { |
| final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics(); |
| return getSuperClassSubstitutor(superClass, classResolveResult.getElement(), classResolveResult.getSubstitutor()); |
| } |
| |
| @Nullable |
| private static PsiSubstitutor getSuperClassSubstitutorInner(@NotNull PsiClass base, |
| @NotNull PsiClass candidate, |
| @NotNull PsiSubstitutor candidateSubstitutor, |
| @NotNull Set<PsiClass> visited, |
| @NotNull PsiManager manager) { |
| if (!visited.add(candidate)) return null; |
| assert candidateSubstitutor.isValid(); |
| |
| if (base == candidate) return candidateSubstitutor; |
| if (manager.areElementsEquivalent(base, candidate)) { |
| PsiTypeParameter[] baseParams = base.getTypeParameters(); |
| PsiTypeParameter[] candidateParams = candidate.getTypeParameters(); |
| PsiElementFactory factory = JavaPsiFacade.getInstance(base.getProject()).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(@NotNull PsiClassType[] types, |
| @NotNull PsiSubstitutor candidateSubstitutor, |
| @NotNull PsiClass base, |
| @NotNull Set<PsiClass> set, |
| @NotNull 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() ? JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createRawSubstitutor(base) : newSubstitutor; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * see JLS 5.6.2 |
| */ |
| @NotNull |
| 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; |
| } |
| |
| @NotNull |
| private static PsiType unbox(@NotNull 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); |
| } |
| @Contract("null -> false") |
| public static boolean isAssignableFromPrimitiveWrapper(final PsiType type) { |
| if (type == null) return false; |
| return isPrimitiveWrapper(type) || |
| type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || |
| type.equalsToText(CommonClassNames.JAVA_LANG_NUMBER); |
| } |
| |
| @Contract("null -> false") |
| public static boolean isPrimitiveWrapper(final PsiType type) { |
| return type != null && isPrimitiveWrapper(type.getCanonicalText()); |
| } |
| |
| @Contract("null -> false") |
| public static boolean isComposite(final PsiType type) { |
| return type instanceof PsiDisjunctionType || type instanceof PsiIntersectionType; |
| } |
| |
| public static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter) { |
| return typeParameterErasure(typeParameter, PsiSubstitutor.EMPTY); |
| } |
| |
| private static PsiType typeParameterErasure(@NotNull PsiTypeParameter typeParameter, @NotNull 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, beforeSubstitutor); |
| } |
| else if (psiClass != null) { |
| return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass); |
| } |
| } |
| return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope()); |
| } |
| |
| private static PsiClassType typeParameterErasureInner(PsiTypeParameter typeParameter, |
| Set<PsiClass> visited, |
| PsiSubstitutor beforeSubstitutor) { |
| 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); |
| if (beforeSubstitutor.getSubstitutionMap().containsKey(psiClass)) { |
| return (PsiClassType)erasure(beforeSubstitutor.substitute((PsiTypeParameter)psiClass)); |
| } |
| return typeParameterErasureInner((PsiTypeParameter)psiClass, visited, beforeSubstitutor); |
| } |
| } |
| else if (psiClass != null) { |
| return JavaPsiFacade.getInstance(typeParameter.getProject()).getElementFactory().createType(psiClass); |
| } |
| } |
| return PsiType.getJavaLangObject(typeParameter.getManager(), typeParameter.getResolveScope()); |
| } |
| |
| @Contract("null -> null") |
| public static PsiType erasure(@Nullable PsiType type) { |
| return erasure(type, PsiSubstitutor.EMPTY); |
| } |
| |
| @Contract("null, _ -> null") |
| public static PsiType erasure(@Nullable final PsiType type, @NotNull final PsiSubstitutor beforeSubstitutor) { |
| if (type == null) return null; |
| return type.accept(new PsiTypeVisitor<PsiType>() { |
| @Override |
| public PsiType visitClassType(PsiClassType classType) { |
| final PsiClass aClass = classType.resolve(); |
| if (aClass instanceof PsiTypeParameter) { |
| return typeParameterErasure((PsiTypeParameter)aClass, beforeSubstitutor); |
| } |
| return classType.rawType(); |
| } |
| |
| @Override |
| public PsiType visitWildcardType(PsiWildcardType wildcardType) { |
| return wildcardType.getExtendsBound().accept(this); |
| } |
| |
| @Override |
| public PsiType visitPrimitiveType(PsiPrimitiveType primitiveType) { |
| return primitiveType; |
| } |
| |
| @Override |
| public PsiType visitEllipsisType(PsiEllipsisType ellipsisType) { |
| final PsiType componentType = ellipsisType.getComponentType(); |
| final PsiType newComponentType = componentType.accept(this); |
| if (newComponentType == componentType) return ellipsisType; |
| return newComponentType != null ? newComponentType.createArrayType() : null; |
| } |
| |
| @Override |
| public PsiType visitArrayType(PsiArrayType arrayType) { |
| final PsiType componentType = arrayType.getComponentType(); |
| final PsiType newComponentType = componentType.accept(this); |
| if (newComponentType == componentType) return arrayType; |
| return newComponentType != null ? newComponentType.createArrayType() : null; |
| } |
| |
| @Override |
| public PsiType visitDisjunctionType(PsiDisjunctionType disjunctionType) { |
| final PsiClassType lub = PsiTypesUtil.getLowestUpperBoundClassType(disjunctionType); |
| return lub != null ? erasure(lub, beforeSubstitutor) : disjunctionType; |
| } |
| }); |
| } |
| |
| 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 && PsiType.BOOLEAN.equals(castType)) { |
| 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; |
| } |
| |
| @NotNull |
| public static PsiType unboxAndBalanceTypes(PsiType type1, PsiType type2) { |
| if (type1 instanceof PsiClassType) type1 = PsiPrimitiveType.getUnboxedType(type1); |
| if (type2 instanceof PsiClassType) type2 = PsiPrimitiveType.getUnboxedType(type2); |
| |
| if (PsiType.DOUBLE.equals(type1) || PsiType.DOUBLE.equals(type2)) return PsiType.DOUBLE; |
| if (PsiType.FLOAT.equals(type1) || PsiType.FLOAT.equals(type2)) return PsiType.FLOAT; |
| if (PsiType.LONG.equals(type1) || PsiType.LONG.equals(type2)) return PsiType.LONG; |
| return PsiType.INT; |
| } |
| |
| public static IElementType convertEQtoOperation(IElementType eqOpSign) { |
| IElementType opSign = null; |
| if (eqOpSign == JavaTokenType.ANDEQ) { |
| opSign = JavaTokenType.AND; |
| } |
| else if (eqOpSign == JavaTokenType.ASTERISKEQ) { |
| opSign = JavaTokenType.ASTERISK; |
| } |
| else if (eqOpSign == JavaTokenType.DIVEQ) { |
| opSign = JavaTokenType.DIV; |
| } |
| else if (eqOpSign == JavaTokenType.GTGTEQ) { |
| opSign = JavaTokenType.GTGT; |
| } |
| else if (eqOpSign == JavaTokenType.GTGTGTEQ) { |
| opSign = JavaTokenType.GTGTGT; |
| } |
| else if (eqOpSign == JavaTokenType.LTLTEQ) { |
| opSign = JavaTokenType.LTLT; |
| } |
| else if (eqOpSign == JavaTokenType.MINUSEQ) { |
| opSign = JavaTokenType.MINUS; |
| } |
| else if (eqOpSign == JavaTokenType.OREQ) { |
| opSign = JavaTokenType.OR; |
| } |
| else if (eqOpSign == JavaTokenType.PERCEQ) { |
| opSign = JavaTokenType.PERC; |
| } |
| else if (eqOpSign == JavaTokenType.PLUSEQ) { |
| opSign = JavaTokenType.PLUS; |
| } |
| else if (eqOpSign == JavaTokenType.XOREQ) { |
| opSign = JavaTokenType.XOR; |
| } |
| return opSign; |
| } |
| |
| @Nullable |
| public static PsiType calcTypeForBinaryExpression(PsiType lType, PsiType rType, @NotNull IElementType sign, boolean accessLType) { |
| if (sign == JavaTokenType.PLUS) { |
| // evaluate right argument first, since '+-/*%' is left associative and left operand tends to be bigger |
| if (rType == null) return null; |
| if (rType.equalsToText(JAVA_LANG_STRING)) { |
| return rType; |
| } |
| if (!accessLType) return NULL_TYPE; |
| if (lType == null) return null; |
| if (lType.equalsToText(JAVA_LANG_STRING)) { |
| return lType; |
| } |
| return unboxAndBalanceTypes(lType, rType); |
| } |
| if (sign == JavaTokenType.MINUS || sign == JavaTokenType.ASTERISK || sign == JavaTokenType.DIV || sign == JavaTokenType.PERC) { |
| if (rType == null) return null; |
| if (!accessLType) return NULL_TYPE; |
| if (lType == null) return null; |
| return unboxAndBalanceTypes(lType, rType); |
| } |
| if (sign == JavaTokenType.LTLT || sign == JavaTokenType.GTGT || sign == JavaTokenType.GTGTGT) { |
| if (!accessLType) return NULL_TYPE; |
| if (PsiType.BYTE.equals(lType) || PsiType.CHAR.equals(lType) || PsiType.SHORT.equals(lType)) { |
| return PsiType.INT; |
| } |
| if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType); |
| return lType; |
| } |
| if (sign == JavaTokenType.EQEQ || |
| sign == JavaTokenType.NE || |
| sign == JavaTokenType.LT || |
| sign == JavaTokenType.GT || |
| sign == JavaTokenType.LE || |
| sign == JavaTokenType.GE || |
| sign == JavaTokenType.OROR || |
| sign == JavaTokenType.ANDAND) { |
| return PsiType.BOOLEAN; |
| } |
| if (sign == JavaTokenType.OR || sign == JavaTokenType.XOR || sign == JavaTokenType.AND) { |
| if (rType instanceof PsiClassType) rType = PsiPrimitiveType.getUnboxedType(rType); |
| |
| if (lType instanceof PsiClassType) lType = PsiPrimitiveType.getUnboxedType(lType); |
| |
| if (rType == null) return null; |
| if (PsiType.BOOLEAN.equals(rType)) return PsiType.BOOLEAN; |
| if (!accessLType) return NULL_TYPE; |
| if (lType == null) return null; |
| if (PsiType.BOOLEAN.equals(lType)) return PsiType.BOOLEAN; |
| if (PsiType.LONG.equals(lType) || PsiType.LONG.equals(rType)) return PsiType.LONG; |
| return PsiType.INT; |
| } |
| LOG.error("Unknown token: "+sign); |
| return null; |
| } |
| |
| /** |
| * See JLS 3.10.2. Floating-Point Literals |
| * @return true if floating point literal consists of zeros only |
| */ |
| public static boolean isFPZero(@NotNull final String text) { |
| for (int i = 0; i < text.length(); i++) { |
| final char c = text.charAt(i); |
| if (Character.isDigit(c) && c != '0') return false; |
| final char d = Character.toUpperCase(c); |
| if (d == 'E' || d == 'P') break; |
| } |
| return true; |
| } |
| |
| private interface Caster { |
| @NotNull |
| Object cast(@NotNull Object operand); |
| } |
| |
| private static final Caster[][] caster = { |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (short)((Number)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (char)((Number)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return ((Number)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (long)((Number)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (float)((Number)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (double)((Number)operand).intValue(); |
| } |
| } |
| } |
| , |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (byte)((Short)operand).shortValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (char)((Short)operand).shortValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (int)(Short)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (long)(Short)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (float)(Short)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (double)(Short)operand; |
| } |
| } |
| } |
| , |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (byte)((Character)operand).charValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (short)((Character)operand).charValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (int)(Character)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (long)(Character)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (float)(Character)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (double)(Character)operand; |
| } |
| } |
| } |
| , |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (byte)((Integer)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (short)((Integer)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (char)((Integer)operand).intValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (long)(Integer)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (float)(Integer)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (double)(Integer)operand; |
| } |
| } |
| } |
| , |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (byte)((Long)operand).longValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (short)((Long)operand).longValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (char)((Long)operand).longValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (int)((Long)operand).longValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (float)(Long)operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (double)(Long)operand; |
| } |
| } |
| } |
| , |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (byte)((Float)operand).floatValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (short)((Float)operand).floatValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (char)((Float)operand).floatValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (int)((Float)operand).floatValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (long)((Float)operand).floatValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (double)(Float)operand; |
| } |
| } |
| } |
| , |
| { |
| new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (byte)((Double)operand).doubleValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (short)((Double)operand).doubleValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (char)((Double)operand).doubleValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (int)((Double)operand).doubleValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return (long)((Double)operand).doubleValue(); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return new Float((Double)operand); |
| } |
| } |
| , new Caster() { |
| @NotNull |
| @Override |
| public Object cast(@NotNull Object operand) { |
| return operand; |
| } |
| } |
| } |
| }; |
| |
| 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(@NotNull Object o) { |
| return WRAPPER_TO_PRIMITIVE.get(o.getClass()); |
| } |
| |
| private static class WildcardDetector extends PsiTypeVisitor<Boolean> { |
| @Override |
| public Boolean visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) { |
| return true; |
| } |
| |
| @Override |
| public Boolean visitWildcardType(PsiWildcardType wildcardType) { |
| return true; |
| } |
| |
| @Override |
| public Boolean visitClassType(PsiClassType classType) { |
| final PsiType[] parameters = classType.getParameters(); |
| for (PsiType parameter : parameters) { |
| if (parameter.accept(this)) return true; |
| } |
| return super.visitClassType(classType); |
| } |
| |
| @Override |
| public Boolean visitArrayType(PsiArrayType arrayType) { |
| return arrayType.getComponentType().accept(this); |
| } |
| |
| @Nullable |
| @Override |
| public Boolean visitIntersectionType(PsiIntersectionType intersectionType) { |
| for (PsiType psiType : intersectionType.getConjuncts()) { |
| if (psiType.accept(this)) return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public Boolean visitType(PsiType type) { |
| return false; |
| } |
| } |
| } |