| /* |
| * 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 org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions; |
| |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.pom.java.LanguageLevel; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.PsiSubstitutorImpl; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.TypeConversionUtil; |
| import com.intellij.util.Function; |
| import com.intellij.util.containers.ComparatorUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.HashMap; |
| import gnu.trove.THashMap; |
| import gnu.trove.TIntObjectHashMap; |
| import gnu.trove.TObjectIntHashMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; |
| import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; |
| import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; |
| import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; |
| import org.jetbrains.plugins.groovy.lang.psi.api.SpreadState; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotation; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationArrayInitializer; |
| import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.annotation.GrAnnotationMemberValue; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature; |
| import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature; |
| import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.*; |
| import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrImmediateClosureSignatureImpl; |
| import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.GrTypeConverter; |
| import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; |
| import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; |
| |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| /** |
| * @author ven |
| */ |
| public class TypesUtil { |
| |
| @NonNls |
| public static final Map<String, PsiType> ourQNameToUnboxed = new HashMap<String, PsiType>(); |
| public static final PsiPrimitiveType[] PRIMITIVES = { |
| PsiType.BYTE, |
| PsiType.CHAR, |
| PsiType.DOUBLE, |
| PsiType.FLOAT, |
| PsiType.INT, |
| PsiType.SHORT, |
| PsiType.LONG, |
| PsiType.BOOLEAN, |
| PsiType.VOID |
| }; |
| |
| private TypesUtil() { |
| } |
| |
| @NotNull |
| public static GroovyResolveResult[] getOverloadedOperatorCandidates(@NotNull PsiType thisType, |
| IElementType tokenType, |
| @NotNull GroovyPsiElement place, |
| PsiType[] argumentTypes) { |
| return getOverloadedOperatorCandidates(thisType, tokenType, place, argumentTypes, false); |
| } |
| |
| @NotNull |
| public static GroovyResolveResult[] getOverloadedOperatorCandidates(@NotNull PsiType thisType, |
| IElementType tokenType, |
| @NotNull GroovyPsiElement place, |
| PsiType[] argumentTypes, |
| boolean incompleteCode) { |
| return ResolveUtil.getMethodCandidates(thisType, ourOperationsToOperatorNames.get(tokenType), place, true, incompleteCode, false, argumentTypes); |
| } |
| |
| |
| public static GroovyResolveResult[] getOverloadedUnaryOperatorCandidates(@NotNull PsiType thisType, |
| IElementType tokenType, |
| @NotNull GroovyPsiElement place, |
| PsiType[] argumentTypes) { |
| return ResolveUtil.getMethodCandidates(thisType, ourUnaryOperationsToOperatorNames.get(tokenType), place, argumentTypes); |
| } |
| |
| private static final Map<IElementType, String> ourPrimitiveTypesToClassNames = new HashMap<IElementType, String>(); |
| private static final String NULL = "null"; |
| |
| static { |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mSTRING_LITERAL, CommonClassNames.JAVA_LANG_STRING); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mGSTRING_LITERAL, CommonClassNames.JAVA_LANG_STRING); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mREGEX_LITERAL, CommonClassNames.JAVA_LANG_STRING); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mDOLLAR_SLASH_REGEX_LITERAL, CommonClassNames.JAVA_LANG_STRING); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_INT, CommonClassNames.JAVA_LANG_INTEGER); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_LONG, CommonClassNames.JAVA_LANG_LONG); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_FLOAT, CommonClassNames.JAVA_LANG_FLOAT); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_DOUBLE, CommonClassNames.JAVA_LANG_DOUBLE); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_BIG_INT, GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.mNUM_BIG_DECIMAL, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kFALSE, CommonClassNames.JAVA_LANG_BOOLEAN); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kTRUE, CommonClassNames.JAVA_LANG_BOOLEAN); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kNULL, NULL); |
| |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kINT, CommonClassNames.JAVA_LANG_INTEGER); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kLONG, CommonClassNames.JAVA_LANG_LONG); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kFLOAT, CommonClassNames.JAVA_LANG_FLOAT); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kDOUBLE, CommonClassNames.JAVA_LANG_DOUBLE); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kBOOLEAN, CommonClassNames.JAVA_LANG_BOOLEAN); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kCHAR, CommonClassNames.JAVA_LANG_CHARACTER); |
| ourPrimitiveTypesToClassNames.put(GroovyTokenTypes.kBYTE, CommonClassNames.JAVA_LANG_BYTE); |
| } |
| |
| private static final Map<IElementType, String> ourOperationsToOperatorNames = new HashMap<IElementType, String>(); |
| private static final Map<IElementType, String> ourUnaryOperationsToOperatorNames = new HashMap<IElementType, String>(); |
| |
| static { |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mPLUS, "plus"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mMINUS, "minus"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mBAND, "and"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mBOR, "or"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mBXOR, "xor"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mDIV, "div"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mMOD, "mod"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mSTAR, "multiply"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.kAS, "asType"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mCOMPARE_TO, "compareTo"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mGT, "compareTo"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mGE, "compareTo"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mLT, "compareTo"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mLE, "compareTo"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mSTAR_STAR, "power"); |
| ourOperationsToOperatorNames.put(GroovyElementTypes.COMPOSITE_LSHIFT_SIGN, "leftShift"); |
| ourOperationsToOperatorNames.put(GroovyElementTypes.COMPOSITE_RSHIFT_SIGN, "rightShift"); |
| ourOperationsToOperatorNames.put(GroovyElementTypes.COMPOSITE_TRIPLE_SHIFT_SIGN, "rightShiftUnsigned"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mEQUAL, "equals"); |
| ourOperationsToOperatorNames.put(GroovyTokenTypes.mNOT_EQUAL, "equals"); |
| |
| ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mLNOT, "asBoolean"); |
| ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mPLUS, "positive"); |
| ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mMINUS, "negative"); |
| ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mDEC, "previous"); |
| ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mINC, "next"); |
| ourUnaryOperationsToOperatorNames.put(GroovyTokenTypes.mBNOT, "bitwiseNegate"); |
| } |
| |
| private static final TObjectIntHashMap<String> TYPE_TO_RANK = new TObjectIntHashMap<String>(); |
| |
| static { |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_BYTE, 1); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_SHORT, 2); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_CHARACTER, 2); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_INTEGER, 3); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_LONG, 4); |
| TYPE_TO_RANK.put(GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER, 5); |
| TYPE_TO_RANK.put(GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL, 6); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_FLOAT, 7); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_DOUBLE, 8); |
| TYPE_TO_RANK.put(CommonClassNames.JAVA_LANG_NUMBER, 9); |
| } |
| |
| static { |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_BOOLEAN, PsiType.BOOLEAN); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_BYTE, PsiType.BYTE); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_CHARACTER, PsiType.CHAR); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_SHORT, PsiType.SHORT); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_INTEGER, PsiType.INT); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_LONG, PsiType.LONG); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_FLOAT, PsiType.FLOAT); |
| ourQNameToUnboxed.put(CommonClassNames.JAVA_LANG_DOUBLE, PsiType.DOUBLE); |
| } |
| |
| |
| private static final TIntObjectHashMap<String> RANK_TO_TYPE = new TIntObjectHashMap<String>(); |
| |
| static { |
| RANK_TO_TYPE.put(1, CommonClassNames.JAVA_LANG_INTEGER); |
| RANK_TO_TYPE.put(2, CommonClassNames.JAVA_LANG_INTEGER); |
| RANK_TO_TYPE.put(3, CommonClassNames.JAVA_LANG_INTEGER); |
| RANK_TO_TYPE.put(4, CommonClassNames.JAVA_LANG_LONG); |
| RANK_TO_TYPE.put(5, GroovyCommonClassNames.JAVA_MATH_BIG_INTEGER); |
| RANK_TO_TYPE.put(6, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL); |
| RANK_TO_TYPE.put(7, CommonClassNames.JAVA_LANG_DOUBLE); |
| RANK_TO_TYPE.put(8, CommonClassNames.JAVA_LANG_DOUBLE); |
| RANK_TO_TYPE.put(9, CommonClassNames.JAVA_LANG_NUMBER); |
| } |
| |
| public static boolean isAssignable(@Nullable PsiType lType, @Nullable PsiType rType, @NotNull PsiElement context) { |
| if (lType == null || rType == null) { |
| return false; |
| } |
| |
| if (rType instanceof PsiIntersectionType) { |
| for (PsiType child : ((PsiIntersectionType)rType).getConjuncts()) { |
| if (isAssignable(lType, child, context)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| if (lType instanceof PsiIntersectionType) { |
| for (PsiType child : ((PsiIntersectionType)lType).getConjuncts()) { |
| if (!isAssignable(child, rType, context)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| if (isAssignableWithoutConversions(lType, rType, context)) { |
| return true; |
| } |
| |
| Boolean byConversionInMethodCall = isAssignableByConversion(lType, rType, context, true); |
| if (byConversionInMethodCall != null) { |
| return byConversionInMethodCall.booleanValue(); |
| } |
| |
| if (lType instanceof PsiPrimitiveType && rType == PsiType.NULL) { //check it because now we will wrap primitive type. |
| return false; |
| } |
| |
| final PsiManager manager = context.getManager(); |
| final GlobalSearchScope scope = context.getResolveScope(); |
| |
| lType = boxPrimitiveType(lType, manager, scope); |
| rType = boxPrimitiveType(rType, manager, scope); |
| if (lType.isAssignableFrom(rType)) { |
| return true; |
| } |
| |
| Boolean byConversion = isAssignableByConversion(lType, rType, context, false); |
| if (byConversion != null) { |
| return byConversion.booleanValue(); |
| } |
| |
| return false; |
| } |
| |
| public static boolean isAssignableByMethodCallConversion(@Nullable PsiType lType, |
| @Nullable PsiType rType, |
| @NotNull PsiElement context) { |
| if (lType == null || rType == null) { |
| return false; |
| } |
| |
| if (rType instanceof PsiIntersectionType) { |
| for (PsiType child : ((PsiIntersectionType)rType).getConjuncts()) { |
| if (isAssignableByMethodCallConversion(lType, child, context)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| if (lType instanceof PsiIntersectionType) { |
| for (PsiType child : ((PsiIntersectionType)lType).getConjuncts()) { |
| if (!isAssignableByMethodCallConversion(child, rType, context)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| if (isAssignableWithoutConversions(lType, rType, context)) { |
| return true; |
| } |
| |
| Boolean byConversion = isAssignableByConversion(lType, rType, context, true); |
| if (byConversion != null) { |
| return byConversion.booleanValue(); |
| } |
| |
| return false; |
| } |
| |
| @Nullable |
| private static Boolean isAssignableByConversion(@NotNull PsiType lType, |
| @NotNull PsiType rType, |
| @NotNull PsiElement context, |
| boolean inMethodCall) { |
| if (context instanceof GroovyPsiElement) { |
| for (GrTypeConverter converter : GrTypeConverter.EP_NAME.getExtensions()) { |
| if (inMethodCall == converter.isAllowedInMethodCall()) { |
| final Boolean result = converter.isConvertible(lType, rType, (GroovyPsiElement)context); |
| if (result != null) { |
| return result; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| public static boolean isAssignableWithoutConversions(@Nullable PsiType lType, |
| @Nullable PsiType rType, |
| @NotNull PsiElement context) { |
| if (lType == null || rType == null) return false; |
| |
| if (rType == PsiType.NULL) { |
| return !(lType instanceof PsiPrimitiveType); |
| } |
| |
| PsiManager manager = context.getManager(); |
| GlobalSearchScope scope = context.getResolveScope(); |
| |
| if (rType instanceof GrTupleType && ((GrTupleType)rType).getComponentTypes().length == 0) { |
| if (lType instanceof PsiArrayType || |
| InheritanceUtil.isInheritor(lType, CommonClassNames.JAVA_UTIL_LIST) || |
| InheritanceUtil.isInheritor(lType, CommonClassNames.JAVA_UTIL_SET)) { |
| return true; |
| } |
| } |
| |
| if (rType instanceof GrTraitType) { |
| if (isAssignableWithoutConversions(lType, ((GrTraitType)rType).getExprType(), context)) return true; |
| for (PsiClassType trait : ((GrTraitType)rType).getTraitTypes()) { |
| if (isAssignableWithoutConversions(lType, trait, context)) return true; |
| } |
| return false; |
| } |
| |
| if (isClassType(rType, GroovyCommonClassNames.GROOVY_LANG_GSTRING) && lType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) { |
| return true; |
| } |
| |
| if (isNumericType(lType) && isNumericType(rType)) { |
| lType = unboxPrimitiveTypeWrapper(lType); |
| if (isClassType(lType, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL)) lType = PsiType.DOUBLE; |
| rType = unboxPrimitiveTypeWrapper(rType); |
| if (isClassType(rType, GroovyCommonClassNames.JAVA_MATH_BIG_DECIMAL)) rType = PsiType.DOUBLE; |
| } |
| else { |
| rType = boxPrimitiveType(rType, manager, scope); |
| lType = boxPrimitiveType(lType, manager, scope); |
| } |
| |
| if (rType instanceof GrMapType || rType instanceof GrTupleType) { |
| Boolean result = isAssignableForNativeTypes(lType, (PsiClassType)rType, context); |
| if (result != null && result.booleanValue()) return true; |
| } |
| |
| if (rType instanceof GrClosureType) { |
| if (canMakeClosureRaw(lType)) { |
| rType = ((GrClosureType)rType).rawType(); |
| } |
| } |
| |
| return TypeConversionUtil.isAssignable(lType, rType); |
| } |
| |
| private static boolean canMakeClosureRaw(PsiType type) { |
| if (!(type instanceof PsiClassType)) return true; |
| |
| final PsiType[] parameters = ((PsiClassType)type).getParameters(); |
| |
| if (parameters.length != 1) return true; |
| |
| final PsiType parameter = parameters[0]; |
| if (parameter instanceof PsiWildcardType) return true; |
| |
| return false; |
| } |
| |
| @Nullable |
| private static Boolean isAssignableForNativeTypes(@NotNull PsiType lType, |
| @NotNull PsiClassType rType, |
| @NotNull PsiElement context) { |
| if (!(lType instanceof PsiClassType)) return null; |
| final PsiClassType.ClassResolveResult leftResult = ((PsiClassType)lType).resolveGenerics(); |
| final PsiClassType.ClassResolveResult rightResult = rType.resolveGenerics(); |
| final PsiClass leftClass = leftResult.getElement(); |
| PsiClass rightClass = rightResult.getElement(); |
| if (rightClass == null || leftClass == null) return null; |
| |
| if (!InheritanceUtil.isInheritorOrSelf(rightClass, leftClass, true)) return Boolean.FALSE; |
| |
| PsiSubstitutor rightSubstitutor = rightResult.getSubstitutor(); |
| |
| if (!leftClass.hasTypeParameters()) return Boolean.TRUE; |
| PsiSubstitutor leftSubstitutor = leftResult.getSubstitutor(); |
| |
| if (!leftClass.getManager().areElementsEquivalent(leftClass, rightClass)) { |
| rightSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(leftClass, rightClass, rightSubstitutor); |
| rightClass = leftClass; |
| } |
| else if (!rightClass.hasTypeParameters()) return Boolean.TRUE; |
| |
| Iterator<PsiTypeParameter> li = PsiUtil.typeParametersIterator(leftClass); |
| Iterator<PsiTypeParameter> ri = PsiUtil.typeParametersIterator(rightClass); |
| while (li.hasNext()) { |
| if (!ri.hasNext()) return Boolean.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) { |
| return Boolean.TRUE; |
| } |
| if (!isAssignableWithoutConversions(typeLeft, typeRight, context)) return Boolean.FALSE; |
| } |
| return Boolean.TRUE; |
| } |
| |
| public static boolean isNumericType(@Nullable PsiType type) { |
| if (type instanceof PsiClassType) { |
| return TYPE_TO_RANK.contains(getQualifiedName(type)); |
| } |
| |
| return type instanceof PsiPrimitiveType && TypeConversionUtil.isNumericType(type); |
| } |
| |
| public static PsiType unboxPrimitiveTypeWrapperAndEraseGenerics(PsiType result) { |
| return TypeConversionUtil.erasure(unboxPrimitiveTypeWrapper(result)); |
| } |
| |
| public static PsiType unboxPrimitiveTypeWrapper(@Nullable PsiType type) { |
| if (type instanceof PsiClassType) { |
| final PsiClass psiClass = ((PsiClassType)type).resolve(); |
| if (psiClass != null) { |
| PsiType unboxed = ourQNameToUnboxed.get(psiClass.getQualifiedName()); |
| if (unboxed != null) type = unboxed; |
| } |
| } |
| return type; |
| } |
| |
| public static PsiType boxPrimitiveType(@Nullable PsiType result, |
| @NotNull PsiManager manager, |
| @NotNull GlobalSearchScope resolveScope, |
| boolean boxVoid) { |
| if (result instanceof PsiPrimitiveType && (boxVoid || result != PsiType.VOID)) { |
| PsiPrimitiveType primitive = (PsiPrimitiveType)result; |
| String boxedTypeName = primitive.getBoxedTypeName(); |
| if (boxedTypeName != null) { |
| return GroovyPsiManager.getInstance(manager.getProject()).createTypeByFQClassName(boxedTypeName, resolveScope); |
| } |
| } |
| |
| return result; |
| } |
| |
| public static PsiType boxPrimitiveType(@Nullable PsiType result, @NotNull PsiManager manager, @NotNull GlobalSearchScope resolveScope) { |
| return boxPrimitiveType(result, manager, resolveScope, false); |
| } |
| |
| @NotNull |
| public static PsiClassType createType(String fqName, @NotNull PsiElement context) { |
| return createTypeByFQClassName(fqName, context); |
| } |
| |
| @NotNull |
| public static PsiClassType getJavaLangObject(@NotNull PsiElement context) { |
| return LazyFqnClassType.getLazyType(CommonClassNames.JAVA_LANG_OBJECT, context); |
| } |
| |
| @Nullable |
| public static PsiType getLeastUpperBoundNullable(@Nullable PsiType type1, @Nullable PsiType type2, @NotNull PsiManager manager) { |
| if (type1 == null) return type2; |
| if (type2 == null) return type1; |
| return getLeastUpperBound(type1, type2, manager); |
| } |
| |
| @Nullable |
| public static PsiType getLeastUpperBoundNullable(@NotNull Iterable<PsiType> collection, @NotNull PsiManager manager) { |
| Iterator<PsiType> iterator = collection.iterator(); |
| if (!iterator.hasNext()) return null; |
| PsiType result = iterator.next(); |
| while (iterator.hasNext()) { |
| result = getLeastUpperBoundNullable(result, iterator.next(), manager); |
| } |
| return result; |
| } |
| |
| @Nullable |
| public static PsiType getLeastUpperBound(@NotNull PsiType type1, @NotNull PsiType type2, PsiManager manager) { |
| if (type1 instanceof GrTupleType && type2 instanceof GrTupleType) { |
| GrTupleType tuple1 = (GrTupleType)type1; |
| GrTupleType tuple2 = (GrTupleType)type2; |
| PsiType[] components1 = tuple1.getComponentTypes(); |
| PsiType[] components2 = tuple2.getComponentTypes(); |
| |
| if (components1.length == 0) return genNewListBy(type2, manager); |
| if (components2.length == 0) return genNewListBy(type1, manager); |
| |
| PsiType[] components3 = PsiType.createArray(Math.min(components1.length, components2.length)); |
| for (int i = 0; i < components3.length; i++) { |
| PsiType c1 = components1[i]; |
| PsiType c2 = components2[i]; |
| if (c1 == null || c2 == null) { |
| components3[i] = null; |
| } |
| else { |
| components3[i] = getLeastUpperBound(c1, c2, manager); |
| } |
| } |
| return new GrImmediateTupleType(components3, JavaPsiFacade.getInstance(manager.getProject()), tuple1.getScope().intersectWith(tuple2.getResolveScope())); |
| } |
| else if (checkEmptyListAndList(type1, type2)) { |
| return genNewListBy(type2, manager); |
| } |
| else if (checkEmptyListAndList(type2, type1)) { |
| return genNewListBy(type1, manager); |
| } |
| else if (type1 instanceof GrMapType && type2 instanceof GrMapType) { |
| return GrMapType.merge(((GrMapType)type1), ((GrMapType)type2)); |
| } |
| else if (checkEmptyMapAndMap(type1, type2)) { |
| return genNewMapBy(type2, manager); |
| } |
| else if (checkEmptyMapAndMap(type2, type1)) { |
| return genNewMapBy(type1, manager); |
| } |
| else if (type1 instanceof GrClosureType && type2 instanceof GrClosureType) { |
| GrClosureType clType1 = (GrClosureType)type1; |
| GrClosureType clType2 = (GrClosureType)type2; |
| GrSignature signature1 = clType1.getSignature(); |
| GrSignature signature2 = clType2.getSignature(); |
| |
| if (signature1 instanceof GrClosureSignature && signature2 instanceof GrClosureSignature) { |
| if (((GrClosureSignature)signature1).getParameterCount() == ((GrClosureSignature)signature2).getParameterCount()) { |
| final GrClosureSignature signature = GrImmediateClosureSignatureImpl.getLeastUpperBound(((GrClosureSignature)signature1), |
| ((GrClosureSignature)signature2), manager); |
| if (signature != null) { |
| GlobalSearchScope scope = clType1.getResolveScope().intersectWith(clType2.getResolveScope()); |
| final LanguageLevel languageLevel = ComparatorUtil.max(clType1.getLanguageLevel(), clType2.getLanguageLevel()); |
| return GrClosureType.create(signature, scope, JavaPsiFacade.getInstance(manager.getProject()), languageLevel, true); |
| } |
| } |
| } |
| } |
| else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(getQualifiedName(type1)) && |
| CommonClassNames.JAVA_LANG_STRING.equals(getQualifiedName(type2))) { |
| return type2; |
| } |
| else if (GroovyCommonClassNames.GROOVY_LANG_GSTRING.equals(getQualifiedName(type2)) && |
| CommonClassNames.JAVA_LANG_STRING.equals(getQualifiedName(type1))) { |
| return type1; |
| } |
| return GenericsUtil.getLeastUpperBound(type1, type2, manager); |
| } |
| |
| private static boolean checkEmptyListAndList(PsiType type1, PsiType type2) { |
| if (type1 instanceof GrTupleType) { |
| PsiType[] types = ((GrTupleType)type1).getComponentTypes(); |
| if (types.length == 0 && InheritanceUtil.isInheritor(type2, CommonClassNames.JAVA_UTIL_LIST)) return true; |
| } |
| |
| return false; |
| } |
| |
| private static PsiType genNewListBy(PsiType genericOwner, PsiManager manager) { |
| PsiClass list = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_UTIL_LIST, genericOwner.getResolveScope()); |
| PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject()); |
| if (list == null) return factory.createTypeFromText(CommonClassNames.JAVA_UTIL_LIST, null); |
| return factory.createType(list, PsiUtil.extractIterableTypeParameter(genericOwner, false)); |
| } |
| |
| private static boolean checkEmptyMapAndMap(PsiType type1, PsiType type2) { |
| if (type1 instanceof GrMapType) { |
| if (((GrMapType)type1).isEmpty() && InheritanceUtil.isInheritor(type2, CommonClassNames.JAVA_UTIL_MAP)) return true; |
| } |
| |
| return false; |
| } |
| |
| private static PsiType genNewMapBy(PsiType genericOwner, PsiManager manager) { |
| PsiClass map = JavaPsiFacade.getInstance(manager.getProject()).findClass(CommonClassNames.JAVA_UTIL_MAP, genericOwner.getResolveScope()); |
| PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject()); |
| if (map == null) return factory.createTypeFromText(CommonClassNames.JAVA_UTIL_MAP, null); |
| |
| final PsiType key = PsiUtil.substituteTypeParameter(genericOwner, CommonClassNames.JAVA_UTIL_MAP, 0, false); |
| final PsiType value = PsiUtil.substituteTypeParameter(genericOwner, CommonClassNames.JAVA_UTIL_MAP, 1, false); |
| return factory.createType(map, key, value); |
| } |
| |
| @Nullable |
| public static PsiType getPsiType(PsiElement context, IElementType elemType) { |
| if (elemType == GroovyTokenTypes.kNULL) { |
| return PsiType.NULL; |
| } |
| final String typeName = getBoxedTypeName(elemType); |
| if (typeName != null) { |
| return createTypeByFQClassName(typeName, context); |
| } |
| return null; |
| } |
| |
| @Nullable |
| public static String getBoxedTypeName(IElementType elemType) { |
| return ourPrimitiveTypesToClassNames.get(elemType); |
| } |
| |
| @NotNull |
| public static PsiType getLeastUpperBound(PsiClass[] classes, PsiManager manager) { |
| PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject()); |
| |
| if (classes.length == 0) return factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_OBJECT); |
| |
| PsiType type = factory.createType(classes[0]); |
| |
| for (int i = 1; i < classes.length; i++) { |
| PsiType t = getLeastUpperBound(type, factory.createType(classes[i]), manager); |
| if (t != null) { |
| type = t; |
| } |
| } |
| |
| return type; |
| } |
| |
| public static boolean isClassType(@Nullable PsiType type, @NotNull String qName) { |
| return qName.equals(getQualifiedName(type)); |
| } |
| |
| public static PsiSubstitutor composeSubstitutors(PsiSubstitutor s1, PsiSubstitutor s2) { |
| final Map<PsiTypeParameter, PsiType> map = s1.getSubstitutionMap(); |
| Map<PsiTypeParameter, PsiType> result = new THashMap<PsiTypeParameter, PsiType>(map.size()); |
| for (PsiTypeParameter parameter : map.keySet()) { |
| result.put(parameter, s2.substitute(map.get(parameter))); |
| } |
| final Map<PsiTypeParameter, PsiType> map2 = s2.getSubstitutionMap(); |
| for (PsiTypeParameter parameter : map2.keySet()) { |
| if (!result.containsKey(parameter)) { |
| result.put(parameter, map2.get(parameter)); |
| } |
| } |
| return PsiSubstitutorImpl.createSubstitutor(result); |
| } |
| |
| @NotNull |
| public static PsiClassType createTypeByFQClassName(@NotNull String fqName, @NotNull PsiElement context) { |
| return GroovyPsiManager.getInstance(context.getProject()).createTypeByFQClassName(fqName, context.getResolveScope()); |
| } |
| |
| @Nullable |
| public static PsiType createJavaLangClassType(@Nullable PsiType type, |
| Project project, |
| GlobalSearchScope resolveScope) { |
| final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); |
| PsiType result = null; |
| PsiClass javaLangClass = facade.findClass(CommonClassNames.JAVA_LANG_CLASS, resolveScope); |
| if (javaLangClass != null) { |
| PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; |
| final PsiTypeParameter[] typeParameters = javaLangClass.getTypeParameters(); |
| if (typeParameters.length == 1) { |
| substitutor = substitutor.put(typeParameters[0], type); |
| } |
| result = facade.getElementFactory().createType(javaLangClass, substitutor); |
| } |
| return result; |
| } |
| |
| @NotNull |
| public static PsiPrimitiveType getPrimitiveTypeByText(String typeText) { |
| for (final PsiPrimitiveType primitive : PRIMITIVES) { |
| if (PsiType.VOID.equals(primitive)) { |
| return primitive; |
| } |
| if (primitive.getCanonicalText().equals(typeText)) { |
| return primitive; |
| } |
| } |
| |
| assert false : "Unknown primitive type"; |
| return null; |
| } |
| |
| @NotNull |
| public static PsiClassType createListType(@NotNull PsiClass elements) { |
| JavaPsiFacade facade = JavaPsiFacade.getInstance(elements.getProject()); |
| GlobalSearchScope resolveScope = elements.getResolveScope(); |
| PsiClass listClass = facade.findClass(CommonClassNames.JAVA_UTIL_LIST, resolveScope); |
| if (listClass == null) { |
| return facade.getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_LIST, resolveScope); |
| } |
| return facade.getElementFactory().createType(listClass, facade.getElementFactory().createType(elements)); |
| } |
| |
| @NotNull |
| public static PsiType createSetType(@NotNull PsiElement context, @NotNull PsiType type) { |
| JavaPsiFacade facade = JavaPsiFacade.getInstance(context.getProject()); |
| GlobalSearchScope resolveScope = context.getResolveScope(); |
| |
| PsiClass setClass = facade.findClass(CommonClassNames.JAVA_UTIL_SET, resolveScope); |
| if (setClass != null && setClass.getTypeParameters().length == 1) { |
| return facade.getElementFactory().createType(setClass, type); |
| } |
| |
| return facade.getElementFactory().createTypeByFQClassName(CommonClassNames.JAVA_UTIL_SET, resolveScope); |
| } |
| |
| public static boolean isAnnotatedCheckHierarchyWithCache(@NotNull PsiClass aClass, @NotNull String annotationFQN) { |
| Map<String, PsiClass> classMap = ClassUtil.getSuperClassesWithCache(aClass); |
| |
| for (PsiClass psiClass : classMap.values()) { |
| PsiModifierList modifierList = psiClass.getModifierList(); |
| if (modifierList != null) { |
| if (modifierList.findAnnotation(annotationFQN) != null) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| @Nullable |
| public static PsiType substituteBoxAndNormalizeType(@Nullable PsiType type, |
| @NotNull PsiSubstitutor substitutor, |
| @Nullable SpreadState state, @NotNull GrExpression expression) { |
| if (type == null) return null; |
| GlobalSearchScope resolveScope = expression.getResolveScope(); |
| PsiManager manager = expression.getManager(); |
| type = substitutor.substitute(type); |
| type = boxPrimitiveType(type, manager, resolveScope); |
| if (type == null) return null; |
| type = PsiImplUtil.normalizeWildcardTypeByPosition(type, expression); |
| type = SpreadState.apply(type, state, expression.getProject()); |
| return type; |
| } |
| |
| @Nullable |
| public static PsiType getItemType(@Nullable PsiType containerType) { |
| if (containerType == null) return null; |
| |
| if (containerType instanceof PsiArrayType) return ((PsiArrayType)containerType).getComponentType(); |
| return PsiUtil.extractIterableTypeParameter(containerType, false); |
| } |
| |
| @Nullable |
| public static PsiType inferAnnotationMemberValueType(final GrAnnotationMemberValue value) { |
| if (value instanceof GrExpression) { |
| return ((GrExpression)value).getType(); |
| } |
| |
| else if (value instanceof GrAnnotation) { |
| final PsiElement resolved = ((GrAnnotation)value).getClassReference().resolve(); |
| if (resolved instanceof PsiClass) { |
| return JavaPsiFacade.getElementFactory(value.getProject()).createType((PsiClass)resolved, PsiSubstitutor.EMPTY); |
| } |
| |
| return null; |
| } |
| |
| else if (value instanceof GrAnnotationArrayInitializer) { |
| return getTupleByAnnotationArrayInitializer((GrAnnotationArrayInitializer)value); |
| } |
| |
| return null; |
| } |
| |
| public static PsiType getTupleByAnnotationArrayInitializer(final GrAnnotationArrayInitializer value) { |
| return new GrTupleType(value.getResolveScope(), JavaPsiFacade.getInstance(value.getProject())) { |
| @NotNull |
| @Override |
| protected PsiType[] inferComponents() { |
| final GrAnnotationMemberValue[] initializers = value.getInitializers(); |
| return ContainerUtil.map(initializers, new Function<GrAnnotationMemberValue, PsiType>() { |
| @Override |
| public PsiType fun(GrAnnotationMemberValue value) { |
| return inferAnnotationMemberValueType(value); |
| } |
| }, PsiType.createArray(initializers.length)); |
| } |
| |
| @Override |
| public boolean isValid() { |
| return value.isValid(); |
| } |
| }; |
| } |
| |
| public static boolean resolvesTo(PsiType type, String fqn) { |
| if (type instanceof PsiClassType) { |
| final PsiClass resolved = ((PsiClassType)type).resolve(); |
| return resolved != null && fqn.equals(resolved.getQualifiedName()); |
| } |
| return false; |
| } |
| |
| @Nullable |
| public static PsiType rawSecondGeneric(PsiType type, Project project) { |
| if (!(type instanceof PsiClassType)) return null; |
| |
| final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics(); |
| final PsiClass element = result.getElement(); |
| if (element == null) return null; |
| |
| final PsiType[] parameters = ((PsiClassType)type).getParameters(); |
| |
| boolean changed = false; |
| for (int i = 0; i < parameters.length; i++) { |
| PsiType parameter = parameters[i]; |
| if (parameter == null) continue; |
| |
| final Ref<PsiType> newParam = new Ref<PsiType>(); |
| parameter.accept(new PsiTypeVisitorEx<Object>() { |
| @Nullable |
| @Override |
| public Object visitClassType(PsiClassType classType) { |
| if (classType.getParameterCount() > 0) { |
| newParam.set(classType.rawType()); |
| } |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Object visitCapturedWildcardType(PsiCapturedWildcardType capturedWildcardType) { |
| newParam.set(capturedWildcardType.getWildcard().getBound()); |
| return null; |
| } |
| |
| @Nullable |
| @Override |
| public Object visitWildcardType(PsiWildcardType wildcardType) { |
| newParam.set(wildcardType.getBound()); |
| return null; |
| } |
| }); |
| |
| if (!newParam.isNull()) { |
| changed = true; |
| parameters[i] = newParam.get(); |
| } |
| } |
| if (!changed) return null; |
| return JavaPsiFacade.getElementFactory(project).createType(element, parameters); |
| } |
| |
| public static boolean isPsiClassTypeToClosure(PsiType type) { |
| if (!(type instanceof PsiClassType)) return false; |
| |
| final PsiClass psiClass = ((PsiClassType)type).resolve(); |
| if (psiClass == null) return false; |
| |
| return GroovyCommonClassNames.GROOVY_LANG_CLOSURE.equals(psiClass.getQualifiedName()); |
| } |
| |
| @Nullable |
| public static String getQualifiedName(@Nullable PsiType type) { |
| if (type instanceof PsiClassType) { |
| PsiClass resolved = ((PsiClassType)type).resolve(); |
| if (resolved instanceof PsiAnonymousClass) { |
| return getQualifiedName(((PsiAnonymousClass)resolved).getBaseClassType()); |
| } |
| if (resolved != null) { |
| return resolved.getQualifiedName(); |
| } |
| else { |
| return PsiNameHelper.getQualifiedClassName(type.getCanonicalText(), true); |
| } |
| } |
| |
| return null; |
| } |
| |
| } |