/* * Copyright 2000-2013 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.LanguageLevelProjectExtension; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.infos.MethodCandidateInfo; import com.intellij.psi.infos.MethodCandidateInfo.ApplicabilityLevel; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.meta.PsiMetaData; import com.intellij.psi.meta.PsiMetaOwner; import com.intellij.psi.search.ProjectScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.util.IncorrectOperationException; import com.intellij.util.TimeoutUtil; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.EmptyIterable; import com.intellij.util.containers.HashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; public final class PsiUtil extends PsiUtilCore { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.util.PsiUtil"); public static final int ACCESS_LEVEL_PUBLIC = 4; public static final int ACCESS_LEVEL_PROTECTED = 3; public static final int ACCESS_LEVEL_PACKAGE_LOCAL = 2; public static final int ACCESS_LEVEL_PRIVATE = 1; public static final Key<Boolean> VALID_VOID_TYPE_IN_CODE_FRAGMENT = Key.create("VALID_VOID_TYPE_IN_CODE_FRAGMENT"); private PsiUtil() {} public static boolean isOnAssignmentLeftHand(@NotNull PsiExpression expr) { PsiElement parent = PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class); return parent instanceof PsiAssignmentExpression && PsiTreeUtil.isAncestor(((PsiAssignmentExpression)parent).getLExpression(), expr, false); } public static boolean isAccessibleFromPackage(@NotNull PsiModifierListOwner element, @NotNull PsiPackage aPackage) { if (element.hasModifierProperty(PsiModifier.PUBLIC)) return true; return !element.hasModifierProperty(PsiModifier.PRIVATE) && JavaPsiFacade.getInstance(element.getProject()).isInPackage(element, aPackage); } public static boolean isAccessedForWriting(@NotNull PsiExpression expr) { if (isOnAssignmentLeftHand(expr)) return true; PsiElement parent = PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class); if (parent instanceof PsiPrefixExpression) { IElementType tokenType = ((PsiPrefixExpression) parent).getOperationTokenType(); return tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS; } else if (parent instanceof PsiPostfixExpression) { IElementType tokenType = ((PsiPostfixExpression) parent).getOperationTokenType(); return tokenType == JavaTokenType.PLUSPLUS || tokenType == JavaTokenType.MINUSMINUS; } else { return false; } } public static boolean isAccessedForReading(@NotNull PsiExpression expr) { PsiElement parent = PsiTreeUtil.skipParentsOfType(expr, PsiParenthesizedExpression.class); return !(parent instanceof PsiAssignmentExpression) || !PsiTreeUtil.isAncestor(((PsiAssignmentExpression)parent).getLExpression(), expr, false) || ((PsiAssignmentExpression)parent).getOperationTokenType() != JavaTokenType.EQ; } public static boolean isAccessible(@NotNull PsiMember member, @NotNull PsiElement place, @Nullable PsiClass accessObjectClass) { return isAccessible(place.getProject(), member, place, accessObjectClass); } public static boolean isAccessible(@NotNull Project project, @NotNull PsiMember member, @NotNull PsiElement place, @Nullable PsiClass accessObjectClass) { return JavaPsiFacade.getInstance(project).getResolveHelper().isAccessible(member, place, accessObjectClass); } @NotNull public static JavaResolveResult getAccessObjectClass(@NotNull PsiExpression expression) { if (expression instanceof PsiSuperExpression) return JavaResolveResult.EMPTY; PsiType type = expression.getType(); if (type instanceof PsiClassType) { return ((PsiClassType)type).resolveGenerics(); } if (type instanceof PsiDisjunctionType) { final PsiType lub = ((PsiDisjunctionType)type).getLeastUpperBound(); if (lub instanceof PsiClassType) { return ((PsiClassType)lub).resolveGenerics(); } } if (type == null && expression instanceof PsiReferenceExpression) { JavaResolveResult resolveResult = ((PsiReferenceExpression)expression).advancedResolve(false); if (resolveResult.getElement() instanceof PsiClass) { return resolveResult; } } return JavaResolveResult.EMPTY; } public static boolean isConstantExpression(@Nullable PsiExpression expression) { if (expression == null) return false; IsConstantExpressionVisitor visitor = new IsConstantExpressionVisitor(); expression.accept(visitor); return visitor.myIsConstant; } // todo: move to PsiThrowsList? public static void addException(@NotNull PsiMethod method, @NotNull @NonNls String exceptionFQName) throws IncorrectOperationException { PsiClass exceptionClass = JavaPsiFacade.getInstance(method.getProject()).findClass(exceptionFQName, method.getResolveScope()); addException(method, exceptionClass, exceptionFQName); } public static void addException(@NotNull PsiMethod method, @NotNull PsiClass exceptionClass) throws IncorrectOperationException { addException(method, exceptionClass, exceptionClass.getQualifiedName()); } private static void addException(@NotNull PsiMethod method, @Nullable PsiClass exceptionClass, @Nullable String exceptionName) throws IncorrectOperationException { assert exceptionClass != null || exceptionName != null : "One of exceptionName, exceptionClass must be not null"; PsiReferenceList throwsList = method.getThrowsList(); PsiJavaCodeReferenceElement[] refs = throwsList.getReferenceElements(); boolean replaced = false; for (PsiJavaCodeReferenceElement ref : refs) { if (ref.isReferenceTo(exceptionClass)) return; PsiClass aClass = (PsiClass)ref.resolve(); if (exceptionClass == null || aClass == null) { continue; } if (aClass.isInheritor(exceptionClass, true)) { if (replaced) { ref.delete(); } else { PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory(); PsiJavaCodeReferenceElement ref1; if (exceptionName != null) { ref1 = factory.createReferenceElementByFQClassName(exceptionName, method.getResolveScope()); } else { PsiClassType type = factory.createType(exceptionClass); ref1 = factory.createReferenceElementByType(type); } ref.replace(ref1); replaced = true; } } else if (exceptionClass.isInheritor(aClass, true)) { return; } } if (replaced) return; PsiElementFactory factory = JavaPsiFacade.getInstance(method.getProject()).getElementFactory(); PsiJavaCodeReferenceElement ref; if (exceptionName != null) { ref = factory.createReferenceElementByFQClassName(exceptionName, method.getResolveScope()); } else { PsiClassType type = factory.createType(exceptionClass); ref = factory.createReferenceElementByType(type); } throwsList.add(ref); } // todo: move to PsiThrowsList? public static void removeException(@NotNull PsiMethod method, @NonNls String exceptionClass) throws IncorrectOperationException { PsiJavaCodeReferenceElement[] refs = method.getThrowsList().getReferenceElements(); for (PsiJavaCodeReferenceElement ref : refs) { if (ref.getCanonicalText().equals(exceptionClass)) { ref.delete(); } } } public static boolean isVariableNameUnique(@NotNull String name, @NotNull PsiElement place) { PsiResolveHelper helper = JavaPsiFacade.getInstance(place.getProject()).getResolveHelper(); return helper.resolveAccessibleReferencedVariable(name, place) == null; } /** * @return enclosing outermost (method or class initializer) body but not higher than scope */ @Nullable public static PsiElement getTopLevelEnclosingCodeBlock(@Nullable PsiElement element, PsiElement scope) { PsiElement blockSoFar = null; while (element != null) { // variable can be defined in for loop initializer PsiElement parent = element.getParent(); if (!(parent instanceof PsiExpression) || parent instanceof PsiLambdaExpression) { if (element instanceof PsiCodeBlock || element instanceof PsiForStatement || element instanceof PsiForeachStatement) { blockSoFar = element; } if (parent instanceof PsiMethod && parent.getParent() instanceof PsiClass && !isLocalOrAnonymousClass((PsiClass)parent.getParent())) break; if (parent instanceof PsiClassInitializer && !(parent.getParent() instanceof PsiAnonymousClass)) break; if (parent instanceof PsiField && ((PsiField) parent).getInitializer() == element) { blockSoFar = element; } if (parent instanceof PsiClassLevelDeclarationStatement) { parent = parent.getParent(); } if (element instanceof PsiClass && !isLocalOrAnonymousClass((PsiClass)element)) { break; } if (element instanceof PsiFile && PsiUtilCore.getTemplateLanguageFile(element) != null) { return element; } } if (element == scope) break; element = parent; } return blockSoFar; } public static boolean isLocalOrAnonymousClass(@NotNull PsiClass psiClass) { return psiClass instanceof PsiAnonymousClass || isLocalClass(psiClass); } public static boolean isLocalClass(@NotNull PsiClass psiClass) { PsiElement parent = psiClass.getParent(); return parent instanceof PsiDeclarationStatement && parent.getParent() instanceof PsiCodeBlock; } public static boolean isAbstractClass(@NotNull PsiClass clazz) { PsiModifierList modifierList = clazz.getModifierList(); return modifierList != null && modifierList.hasModifierProperty(PsiModifier.ABSTRACT); } /** * @return topmost code block where variable makes sense */ @Nullable public static PsiElement getVariableCodeBlock(@NotNull PsiVariable variable, @Nullable PsiElement context) { PsiElement codeBlock = null; if (variable instanceof PsiParameter) { PsiElement declarationScope = ((PsiParameter)variable).getDeclarationScope(); if (declarationScope instanceof PsiCatchSection) { codeBlock = ((PsiCatchSection)declarationScope).getCatchBlock(); } else if (declarationScope instanceof PsiForeachStatement) { codeBlock = ((PsiForeachStatement)declarationScope).getBody(); } else if (declarationScope instanceof PsiMethod) { codeBlock = ((PsiMethod)declarationScope).getBody(); } else if (declarationScope instanceof PsiLambdaExpression) { codeBlock = ((PsiLambdaExpression)declarationScope).getBody(); } } else if (variable instanceof PsiResourceVariable) { final PsiElement resourceList = variable.getParent(); return resourceList != null ? resourceList.getParent() : null; // use try statement as topmost } else if (variable instanceof PsiLocalVariable && variable.getParent() instanceof PsiForStatement) { return variable.getParent(); } else if (variable instanceof PsiField && context != null) { final PsiClass aClass = ((PsiField) variable).getContainingClass(); while (context != null && context.getParent() != aClass) { context = context.getParent(); if (context instanceof PsiClassLevelDeclarationStatement) return null; } return context instanceof PsiMethod ? ((PsiMethod) context).getBody() : context instanceof PsiClassInitializer ? ((PsiClassInitializer) context).getBody() : null; } else { final PsiElement scope = variable.getParent() == null ? null : variable.getParent().getParent(); codeBlock = getTopLevelEnclosingCodeBlock(variable, scope); if (codeBlock != null && codeBlock.getParent() instanceof PsiSwitchStatement) codeBlock = codeBlock.getParent().getParent(); } return codeBlock; } public static boolean isIncrementDecrementOperation(@NotNull PsiElement element) { if (element instanceof PsiPostfixExpression) { final IElementType sign = ((PsiPostfixExpression)element).getOperationTokenType(); if (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS) return true; } else if (element instanceof PsiPrefixExpression) { final IElementType sign = ((PsiPrefixExpression)element).getOperationTokenType(); if (sign == JavaTokenType.PLUSPLUS || sign == JavaTokenType.MINUSMINUS) return true; } return false; } public static int getAccessLevel(@NotNull PsiModifierList modifierList) { if (modifierList.hasModifierProperty(PsiModifier.PRIVATE)) { return ACCESS_LEVEL_PRIVATE; } else if (modifierList.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { return ACCESS_LEVEL_PACKAGE_LOCAL; } else if (modifierList.hasModifierProperty(PsiModifier.PROTECTED)) { return ACCESS_LEVEL_PROTECTED; } else { return ACCESS_LEVEL_PUBLIC; } } @PsiModifier.ModifierConstant @Nullable public static String getAccessModifier(int accessLevel) { @SuppressWarnings("UnnecessaryLocalVariable") @PsiModifier.ModifierConstant final String modifier = accessLevel > accessModifiers.length ? null : accessModifiers[accessLevel - 1]; return modifier; } private static final String[] accessModifiers = { PsiModifier.PRIVATE, PsiModifier.PACKAGE_LOCAL, PsiModifier.PROTECTED, PsiModifier.PUBLIC }; /** * @return true if element specified is statement or expression statement. see JLS 14.5-14.8 */ public static boolean isStatement(@NotNull PsiElement element) { PsiElement parent = element.getParent(); if (element instanceof PsiExpressionListStatement) { // statement list allowed in for() init or update only if (!(parent instanceof PsiForStatement)) return false; final PsiForStatement forStatement = (PsiForStatement)parent; if (!(element == forStatement.getInitialization() || element == forStatement.getUpdate())) return false; final PsiExpressionList expressionList = ((PsiExpressionListStatement) element).getExpressionList(); final PsiExpression[] expressions = expressionList.getExpressions(); for (PsiExpression expression : expressions) { if (!isStatement(expression)) return false; } return true; } else if (element instanceof PsiExpressionStatement) { return isStatement(((PsiExpressionStatement) element).getExpression()); } if (element instanceof PsiDeclarationStatement) { if (parent instanceof PsiCodeBlock) return true; if (parent instanceof PsiCodeFragment) return true; if (!(parent instanceof PsiForStatement) || ((PsiForStatement)parent).getBody() == element) { return false; } } if (element instanceof PsiStatement) return true; if (element instanceof PsiAssignmentExpression) return true; if (isIncrementDecrementOperation(element)) return true; if (element instanceof PsiMethodCallExpression) return true; if (element instanceof PsiNewExpression) { return !(((PsiNewExpression) element).getType() instanceof PsiArrayType); } return element instanceof PsiCodeBlock; } @Nullable public static PsiElement getEnclosingStatement(PsiElement element) { while (element != null) { if (element.getParent() instanceof PsiCodeBlock) return element; element = element.getParent(); } return null; } @Nullable public static PsiElement getElementInclusiveRange(@NotNull PsiElement scope, @NotNull TextRange range) { PsiElement psiElement = scope.findElementAt(range.getStartOffset()); while (psiElement != null && !psiElement.getTextRange().contains(range)) { if (psiElement == scope) return null; psiElement = psiElement.getParent(); } return psiElement; } @Nullable public static PsiClass resolveClassInType(@Nullable PsiType type) { if (type instanceof PsiClassType) { return ((PsiClassType) type).resolve(); } if (type instanceof PsiArrayType) { return resolveClassInType(((PsiArrayType) type).getComponentType()); } if (type instanceof PsiDisjunctionType) { final PsiType lub = ((PsiDisjunctionType)type).getLeastUpperBound(); if (lub instanceof PsiClassType) { return ((PsiClassType)lub).resolve(); } } return null; } @Nullable public static PsiClass resolveClassInClassTypeOnly(@Nullable PsiType type) { return type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null; } public static PsiClassType.ClassResolveResult resolveGenericsClassInType(@Nullable PsiType type) { if (type instanceof PsiClassType) { final PsiClassType classType = (PsiClassType) type; return classType.resolveGenerics(); } if (type instanceof PsiArrayType) { return resolveGenericsClassInType(((PsiArrayType) type).getComponentType()); } if (type instanceof PsiDisjunctionType) { final PsiType lub = ((PsiDisjunctionType)type).getLeastUpperBound(); if (lub instanceof PsiClassType) { return ((PsiClassType)lub).resolveGenerics(); } } return PsiClassType.ClassResolveResult.EMPTY; } @NotNull public static PsiType convertAnonymousToBaseType(@NotNull PsiType type) { PsiClass psiClass = resolveClassInType(type); if (psiClass instanceof PsiAnonymousClass) { int dims = type.getArrayDimensions(); type = ((PsiAnonymousClass) psiClass).getBaseClassType(); while (dims != 0) { type = type.createArrayType(); dims--; } } return type; } public static boolean isApplicable(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutorForMethod, @NotNull PsiExpressionList argList) { return getApplicabilityLevel(method, substitutorForMethod, argList) != ApplicabilityLevel.NOT_APPLICABLE; } public static boolean isApplicable(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutorForMethod, @NotNull PsiExpression[] argList) { final PsiType[] types = ContainerUtil.map2Array(argList, PsiType.class, PsiExpression.EXPRESSION_TO_TYPE); return getApplicabilityLevel(method, substitutorForMethod, types, getLanguageLevel(method)) != ApplicabilityLevel.NOT_APPLICABLE; } @MethodCandidateInfo.ApplicabilityLevelConstant public static int getApplicabilityLevel(@NotNull PsiMethod method, @NotNull PsiSubstitutor substitutorForMethod, @NotNull PsiExpressionList argList) { return getApplicabilityLevel(method, substitutorForMethod, argList.getExpressionTypes(), getLanguageLevel(argList)); } @MethodCandidateInfo.ApplicabilityLevelConstant public static int getApplicabilityLevel(@NotNull final PsiMethod method, @NotNull final PsiSubstitutor substitutorForMethod, @NotNull final PsiType[] args, @NotNull final LanguageLevel languageLevel) { return getApplicabilityLevel(method, substitutorForMethod, args, languageLevel, true, true); } @MethodCandidateInfo.ApplicabilityLevelConstant public static int getApplicabilityLevel(@NotNull final PsiMethod method, @NotNull final PsiSubstitutor substitutorForMethod, @NotNull final PsiType[] args, @NotNull final LanguageLevel languageLevel, final boolean allowUncheckedConversion, final boolean checkVarargs) { final PsiParameter[] parms = method.getParameterList().getParameters(); if (args.length < parms.length - 1) return ApplicabilityLevel.NOT_APPLICABLE; final PsiClass containingClass = method.getContainingClass(); final boolean isRaw = containingClass != null && isRawSubstitutor(method, substitutorForMethod) && isRawSubstitutor(containingClass, substitutorForMethod); if (!areFirstArgumentsApplicable(args, parms, languageLevel, substitutorForMethod, isRaw)) return ApplicabilityLevel.NOT_APPLICABLE; if (args.length == parms.length) { if (parms.length == 0) return ApplicabilityLevel.FIXED_ARITY; PsiType parmType = getParameterType(parms[parms.length - 1], languageLevel, substitutorForMethod); PsiType argType = args[args.length - 1]; if (argType == null) return ApplicabilityLevel.NOT_APPLICABLE; if (TypeConversionUtil.isAssignable(parmType, argType, allowUncheckedConversion)) return ApplicabilityLevel.FIXED_ARITY; if (isRaw) { final PsiType erasedParamType = TypeConversionUtil.erasure(parmType); final PsiType erasedArgType = TypeConversionUtil.erasure(argType); if (erasedArgType != null && erasedParamType != null && TypeConversionUtil.isAssignable(erasedParamType, erasedArgType)) { return ApplicabilityLevel.FIXED_ARITY; } } } if (checkVarargs && method.isVarArgs() && languageLevel.compareTo(LanguageLevel.JDK_1_5) >= 0) { if (args.length < parms.length) return ApplicabilityLevel.VARARGS; PsiParameter lastParameter = parms[parms.length - 1]; if (!lastParameter.isVarArgs()) return ApplicabilityLevel.NOT_APPLICABLE; PsiType lastParmType = getParameterType(lastParameter, languageLevel, substitutorForMethod); if (!(lastParmType instanceof PsiArrayType)) return ApplicabilityLevel.NOT_APPLICABLE; lastParmType = ((PsiArrayType)lastParmType).getComponentType(); if (lastParmType instanceof PsiCapturedWildcardType) { lastParmType = ((PsiCapturedWildcardType)lastParmType).getWildcard(); } for (int i = parms.length - 1; i < args.length; i++) { PsiType argType = args[i]; if (argType == null || !TypeConversionUtil.isAssignable(lastParmType, argType)) { return ApplicabilityLevel.NOT_APPLICABLE; } } return ApplicabilityLevel.VARARGS; } return ApplicabilityLevel.NOT_APPLICABLE; } private static boolean areFirstArgumentsApplicable(@NotNull PsiType[] args, @NotNull final PsiParameter[] parms, @NotNull LanguageLevel languageLevel, @NotNull final PsiSubstitutor substitutorForMethod, boolean isRaw) { for (int i = 0; i < parms.length - 1; i++) { final PsiType type = args[i]; if (type == null) return false; final PsiParameter parameter = parms[i]; final PsiType substitutedParmType = getParameterType(parameter, languageLevel, substitutorForMethod); if (isRaw) { final PsiType substErasure = TypeConversionUtil.erasure(substitutedParmType); final PsiType typeErasure = TypeConversionUtil.erasure(type); if (substErasure != null && typeErasure != null && !TypeConversionUtil.isAssignable(substErasure, typeErasure)) { return false; } } else if (!TypeConversionUtil.isAssignable(substitutedParmType, type)) { return false; } } return true; } private static PsiType getParameterType(@NotNull final PsiParameter parameter, @NotNull LanguageLevel languageLevel, @NotNull final PsiSubstitutor substitutor) { PsiType parmType = parameter.getType(); if (parmType instanceof PsiClassType) { parmType = ((PsiClassType)parmType).setLanguageLevel(languageLevel); } return substitutor.substitute(parmType); } public static boolean equalOnClass(@NotNull PsiSubstitutor s1, @NotNull PsiSubstitutor s2, @NotNull PsiClass aClass) { return equalOnEquivalentClasses(s1, aClass, s2, aClass); } public static boolean equalOnEquivalentClasses(@NotNull PsiSubstitutor s1, @NotNull PsiClass aClass, @NotNull PsiSubstitutor s2, @NotNull PsiClass bClass) { // assume generic class equals to non-generic if (aClass.hasTypeParameters() != bClass.hasTypeParameters()) return true; final PsiTypeParameter[] typeParameters1 = aClass.getTypeParameters(); final PsiTypeParameter[] typeParameters2 = bClass.getTypeParameters(); if (typeParameters1.length != typeParameters2.length) return false; for (int i = 0; i < typeParameters1.length; i++) { final PsiType substituted2 = s2.substitute(typeParameters2[i]); if (!Comparing.equal(s1.substituteWithBoundsPromotion(typeParameters1[i]), substituted2) && !Comparing.equal(s1.substitute(typeParameters1[i]), substituted2)) return false; } if (aClass.hasModifierProperty(PsiModifier.STATIC)) return true; final PsiClass containingClass1 = aClass.getContainingClass(); final PsiClass containingClass2 = bClass.getContainingClass(); if (containingClass1 != null && containingClass2 != null) { return equalOnEquivalentClasses(s1, containingClass1, s2, containingClass2); } return containingClass1 == null && containingClass2 == null; } /** * JLS 15.28 */ public static boolean isCompileTimeConstant(@NotNull final PsiField field) { return field.hasModifierProperty(PsiModifier.FINAL) && (TypeConversionUtil.isPrimitiveAndNotNull(field.getType()) || field.getType().equalsToText("java.lang.String")) && field.hasInitializer() && isConstantExpression(field.getInitializer()); } public static boolean allMethodsHaveSameSignature(@NotNull PsiMethod[] methods) { if (methods.length == 0) return true; final MethodSignature methodSignature = methods[0].getSignature(PsiSubstitutor.EMPTY); for (int i = 1; i < methods.length; i++) { PsiMethod method = methods[i]; if (!methodSignature.equals(method.getSignature(PsiSubstitutor.EMPTY))) return false; } return true; } @Nullable public static PsiExpression deparenthesizeExpression(PsiExpression expression) { while (true) { if (expression instanceof PsiParenthesizedExpression) { expression = ((PsiParenthesizedExpression)expression).getExpression(); continue; } if (expression instanceof PsiTypeCastExpression) { expression = ((PsiTypeCastExpression)expression).getOperand(); continue; } return expression; } } /** * Checks whether given class is inner (as opposed to nested) * */ public static boolean isInnerClass(@NotNull PsiClass aClass) { return !aClass.hasModifierProperty(PsiModifier.STATIC) && aClass.getContainingClass() != null; } @Nullable public static PsiElement findModifierInList(@NotNull final PsiModifierList modifierList, @NonNls String modifier) { final PsiElement[] children = modifierList.getChildren(); for (PsiElement child : children) { if (child.getText().equals(modifier)) return child; } return null; } @Nullable public static PsiClass getTopLevelClass(@NotNull PsiElement element) { final PsiFile file = element.getContainingFile(); if (file instanceof PsiClassOwner) { final PsiClass[] classes = ((PsiClassOwner)file).getClasses(); for (PsiClass aClass : classes) { if (PsiTreeUtil.isAncestor(aClass, element, false)) return aClass; } } return null; } /** * @param place place to start traversal * @param aClass level to stop traversal * @return element with static modifier enclosing place and enclosed by aClass (if not null) */ @Nullable public static PsiModifierListOwner getEnclosingStaticElement(@NotNull PsiElement place, @Nullable PsiClass aClass) { LOG.assertTrue(aClass == null || !place.isPhysical() || PsiTreeUtil.isContextAncestor(aClass, place, false)); PsiElement parent = place; while (parent != aClass) { if (parent instanceof PsiFile) break; if (parent instanceof PsiModifierListOwner && ((PsiModifierListOwner)parent).hasModifierProperty(PsiModifier.STATIC)) { return (PsiModifierListOwner)parent; } parent = parent.getParent(); } return null; } @Nullable public static PsiType getTypeByPsiElement(@NotNull final PsiElement element) { if (element instanceof PsiVariable) { return ((PsiVariable)element).getType(); } else if (element instanceof PsiMethod) return ((PsiMethod)element).getReturnType(); return null; } @NotNull public static PsiType captureToplevelWildcards(@NotNull final PsiType type, final PsiElement context) { if (type instanceof PsiClassType) { final PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics(); final PsiClass aClass = result.getElement(); if (aClass != null) { final PsiSubstitutor substitutor = result.getSubstitutor(); Map<PsiTypeParameter, PsiType> substitutionMap = null; for (PsiTypeParameter typeParameter : typeParametersIterable(aClass)) { final PsiType substituted = substitutor.substitute(typeParameter); if (substituted instanceof PsiWildcardType) { if (substitutionMap == null) substitutionMap = new HashMap<PsiTypeParameter, PsiType>(substitutor.getSubstitutionMap()); substitutionMap.put(typeParameter, PsiCapturedWildcardType.create((PsiWildcardType)substituted, context)); } } if (substitutionMap != null) { final PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory(); final PsiSubstitutor newSubstitutor = factory.createSubstitutor(substitutionMap); return factory.createType(aClass, newSubstitutor); } } } else if (type instanceof PsiArrayType) { return captureToplevelWildcards(((PsiArrayType)type).getComponentType(), context).createArrayType(); } return type; } public static boolean isInsideJavadocComment(PsiElement element) { return PsiTreeUtil.getParentOfType(element, PsiDocComment.class, true) != null; } @NotNull public static List<PsiTypeElement> getParameterTypeElements(@NotNull PsiParameter parameter) { PsiTypeElement typeElement = parameter.getTypeElement(); return typeElement != null && typeElement.getType() instanceof PsiDisjunctionType ? PsiTreeUtil.getChildrenOfTypeAsList(typeElement, PsiTypeElement.class) : Collections.singletonList(typeElement); } public static void checkIsIdentifier(@NotNull PsiManager manager, String text) throws IncorrectOperationException{ if (!JavaPsiFacade.getInstance(manager.getProject()).getNameHelper().isIdentifier(text)){ throw new IncorrectOperationException(PsiBundle.message("0.is.not.an.identifier", text) ); } } @Nullable public static VirtualFile getJarFile(@NotNull PsiElement candidate) { VirtualFile file = candidate.getContainingFile().getVirtualFile(); if (file != null && file.getFileSystem().getProtocol().equals("jar")) { return VfsUtilCore.getVirtualFileForJar(file); } return file; } public static boolean isAnnotationMethod(PsiElement element) { if (!(element instanceof PsiAnnotationMethod)) return false; PsiClass psiClass = ((PsiAnnotationMethod)element).getContainingClass(); return psiClass != null && psiClass.isAnnotationType(); } @PsiModifier.ModifierConstant public static String getMaximumModifierForMember(final PsiClass aClass) { return getMaximumModifierForMember(aClass, true); } @PsiModifier.ModifierConstant public static String getMaximumModifierForMember(final PsiClass aClass, boolean allowPublicAbstract) { String modifier = PsiModifier.PUBLIC; if (!allowPublicAbstract && aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !aClass.isEnum()) { modifier = PsiModifier.PROTECTED; } else if (aClass.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || aClass.isEnum()) { modifier = PsiModifier.PACKAGE_LOCAL; } else if (aClass.hasModifierProperty(PsiModifier.PRIVATE)) { modifier = PsiModifier.PRIVATE; } return modifier; } /* * Returns iterator of type parameters visible in owner. Type parameters are iterated in * inner-to-outer, right-to-left order. */ @NotNull public static Iterator<PsiTypeParameter> typeParametersIterator(@NotNull PsiTypeParameterListOwner owner) { return typeParametersIterable(owner).iterator(); } @NotNull public static Iterable<PsiTypeParameter> typeParametersIterable(@NotNull final PsiTypeParameterListOwner owner) { ArrayList<PsiTypeParameter> result = null; PsiTypeParameterListOwner currentOwner = owner; while (currentOwner != null) { PsiTypeParameter[] typeParameters = currentOwner.getTypeParameters(); if (typeParameters.length > 0) { if (result == null) result = new ArrayList<PsiTypeParameter>(typeParameters.length); for (int i = typeParameters.length - 1; i >= 0; i--) { result.add(typeParameters[i]); } } if (currentOwner.hasModifierProperty(PsiModifier.STATIC)) break; currentOwner = currentOwner.getContainingClass(); } if (result == null) return EmptyIterable.getInstance(); return result; } public static boolean canBeOverriden(@NotNull PsiMethod method) { PsiClass parentClass = method.getContainingClass(); return parentClass != null && !method.isConstructor() && !method.hasModifierProperty(PsiModifier.STATIC) && !method.hasModifierProperty(PsiModifier.FINAL) && !method.hasModifierProperty(PsiModifier.PRIVATE) && !(parentClass instanceof PsiAnonymousClass) && !parentClass.hasModifierProperty(PsiModifier.FINAL); } @NotNull public static PsiElement[] mapElements(@NotNull ResolveResult[] candidates) { PsiElement[] result = new PsiElement[candidates.length]; for (int i = 0; i < candidates.length; i++) { result[i] = candidates[i].getElement(); } return result; } @Nullable public static PsiMember findEnclosingConstructorOrInitializer(PsiElement expression) { PsiMember parent = PsiTreeUtil.getParentOfType(expression, PsiClassInitializer.class, PsiMethod.class); if (parent instanceof PsiMethod && !((PsiMethod)parent).isConstructor()) return null; return parent; } public static boolean checkName(@NotNull PsiElement element, @NotNull String name, final PsiElement context) { if (element instanceof PsiMetaOwner) { final PsiMetaData data = ((PsiMetaOwner) element).getMetaData(); if (data != null) return name.equals(data.getName(context)); } return element instanceof PsiNamedElement && name.equals(((PsiNamedElement)element).getName()); } public static boolean isRawSubstitutor (@NotNull PsiTypeParameterListOwner owner, @NotNull PsiSubstitutor substitutor) { for (PsiTypeParameter parameter : typeParametersIterable(owner)) { if (substitutor.substitute(parameter) == null) return true; } return false; } public static final Key<LanguageLevel> FILE_LANGUAGE_LEVEL_KEY = Key.create("FORCE_LANGUAGE_LEVEL"); public static boolean isLanguageLevel5OrHigher(@NotNull final PsiElement element) { return getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_1_5); } public static boolean isLanguageLevel6OrHigher(@NotNull final PsiElement element) { return getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_1_6); } public static boolean isLanguageLevel7OrHigher(@NotNull final PsiElement element) { return getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_1_7); } public static boolean isLanguageLevel8OrHigher(@NotNull final PsiElement element) { return getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_1_8); } @NotNull public static LanguageLevel getLanguageLevel(@NotNull PsiElement element) { if (element instanceof PsiDirectory) { return JavaDirectoryService.getInstance().getLanguageLevel((PsiDirectory)element); } PsiFile file = element.getContainingFile(); if (file instanceof PsiJavaFile) { return ((PsiJavaFile)file).getLanguageLevel(); } if (file != null) { PsiElement context = file.getContext(); if (context != null) { return getLanguageLevel(context); } } return getLanguageLevel(element.getProject()); } @NotNull public static LanguageLevel getLanguageLevel(@NotNull Project project) { LanguageLevelProjectExtension instance = LanguageLevelProjectExtension.getInstance(project); return instance != null ? instance.getLanguageLevel() : LanguageLevel.HIGHEST; } public static boolean isInstantiatable(@NotNull PsiClass clazz) { return !clazz.hasModifierProperty(PsiModifier.ABSTRACT) && clazz.hasModifierProperty(PsiModifier.PUBLIC) && hasDefaultConstructor(clazz); } public static boolean hasDefaultConstructor(@NotNull PsiClass clazz) { return hasDefaultConstructor(clazz, false); } public static boolean hasDefaultConstructor(@NotNull PsiClass clazz, boolean allowProtected) { return hasDefaultConstructor(clazz, allowProtected, true); } public static boolean hasDefaultConstructor(@NotNull PsiClass clazz, boolean allowProtected, boolean checkModifiers) { return hasDefaultCtrInHierarchy(clazz, allowProtected, checkModifiers, null); } private static boolean hasDefaultCtrInHierarchy(@NotNull PsiClass clazz, boolean allowProtected, boolean checkModifiers, @Nullable Set<PsiClass> visited) { final PsiMethod[] constructors = clazz.getConstructors(); if (constructors.length > 0) { for (PsiMethod cls: constructors) { if ((!checkModifiers || cls.hasModifierProperty(PsiModifier.PUBLIC) || allowProtected && cls.hasModifierProperty(PsiModifier.PROTECTED)) && cls.getParameterList().getParametersCount() == 0) { return true; } } } else { final PsiClass superClass = clazz.getSuperClass(); if (superClass == null) { return true; } if (visited == null) visited = new THashSet<PsiClass>(); if (!visited.add(clazz)) return false; return hasDefaultCtrInHierarchy(superClass, true, true, visited); } return false; } @Nullable public static PsiType extractIterableTypeParameter(@Nullable PsiType psiType, final boolean eraseTypeParameter) { final PsiType type = substituteTypeParameter(psiType, CommonClassNames.JAVA_LANG_ITERABLE, 0, eraseTypeParameter); return type != null ? type : substituteTypeParameter(psiType, CommonClassNames.JAVA_UTIL_COLLECTION, 0, eraseTypeParameter); } @Nullable public static PsiType substituteTypeParameter(@Nullable final PsiType psiType, @NotNull final String superClass, final int typeParamIndex, final boolean eraseTypeParameter) { if (psiType == null) return null; if (!(psiType instanceof PsiClassType)) return null; final PsiClassType classType = (PsiClassType)psiType; final PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics(); final PsiClass psiClass = classResolveResult.getElement(); if (psiClass == null) return null; final PsiClass baseClass = JavaPsiFacade.getInstance(psiClass.getProject()).findClass(superClass, psiClass.getResolveScope()); if (baseClass == null) return null; if (!psiClass.isEquivalentTo(baseClass) && !psiClass.isInheritor(baseClass, true)) return null; final PsiTypeParameter[] parameters = baseClass.getTypeParameters(); if (parameters.length <= typeParamIndex) return PsiType.getJavaLangObject(psiClass.getManager(), psiClass.getResolveScope()); final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, psiClass, classResolveResult.getSubstitutor()); final PsiType type = substitutor.substitute(parameters[typeParamIndex]); if (type == null && eraseTypeParameter) { return TypeConversionUtil.typeParameterErasure(parameters[typeParamIndex]); } return type; } public static final Comparator<PsiElement> BY_POSITION = new Comparator<PsiElement>() { @Override public int compare(PsiElement o1, PsiElement o2) { return compareElementsByPosition(o1, o2); } }; public static void setModifierProperty(@NotNull PsiModifierListOwner owner, @NotNull @PsiModifier.ModifierConstant String property, boolean value) { final PsiModifierList modifierList = owner.getModifierList(); assert modifierList != null : owner; modifierList.setModifierProperty(property, value); } public static boolean isTryBlock(@Nullable final PsiElement element) { if (element == null) return false; final PsiElement parent = element.getParent(); return parent instanceof PsiTryStatement && element == ((PsiTryStatement)parent).getTryBlock(); } public static boolean isElseBlock(@Nullable final PsiElement element) { if (element == null) return false; final PsiElement parent = element.getParent(); return parent instanceof PsiIfStatement && element == ((PsiIfStatement)parent).getElseBranch(); } public static boolean isJavaToken(@Nullable PsiElement element, IElementType type) { return element instanceof PsiJavaToken && ((PsiJavaToken)element).getTokenType() == type; } public static boolean isJavaToken(@Nullable PsiElement element, @NotNull TokenSet types) { return element instanceof PsiJavaToken && types.contains(((PsiJavaToken)element).getTokenType()); } public static boolean isCatchParameter(@Nullable final PsiElement element) { return element instanceof PsiParameter && element.getParent() instanceof PsiCatchSection; } public static boolean isIgnoredName(@Nullable final String name) { return "ignore".equals(name) || "ignored".equals(name); } @Nullable public static PsiMethod getResourceCloserMethod(@NotNull final PsiResourceVariable resource) { final PsiType resourceType = resource.getType(); if (!(resourceType instanceof PsiClassType)) return null; final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); if (resourceClass == null) return null; final Project project = resource.getProject(); final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); final PsiClass autoCloseable = facade.findClass(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, ProjectScope.getLibrariesScope(project)); if (autoCloseable == null) return null; if (!InheritanceUtil.isInheritorOrSelf(resourceClass, autoCloseable, true)) return null; final PsiMethod[] closes = autoCloseable.findMethodsByName("close", false); return closes.length == 1 ? resourceClass.findMethodBySignature(closes[0], true) : null; } @Nullable public static PsiExpression skipParenthesizedExprDown(PsiExpression initializer) { while (initializer instanceof PsiParenthesizedExpression) { initializer = ((PsiParenthesizedExpression)initializer).getExpression(); } return initializer; } public static PsiElement skipParenthesizedExprUp(PsiElement parent) { while (parent instanceof PsiParenthesizedExpression) { parent = parent.getParent(); } return parent; } public static void ensureValidType(@NotNull PsiType type) { ensureValidType(type, null); } public static void ensureValidType(@NotNull PsiType type, @Nullable String customMessage) { if (!type.isValid()) { TimeoutUtil.sleep(1); // to see if processing in another thread suddenly makes the type valid again (which is a bug) if (type.isValid()) { LOG.error("PsiType resurrected: " + type + " of " + type.getClass() + " " + customMessage); return; } if (type instanceof PsiClassType) { try { PsiClass psiClass = ((PsiClassType)type).resolve(); // should throw exception if (psiClass != null) { ensureValid(psiClass); } } catch (PsiInvalidElementAccessException e) { throw customMessage == null? e : new RuntimeException(customMessage, e); } } throw new AssertionError("Invalid type: " + type + " of class " + type.getClass() + " " + customMessage); } } @Nullable public static String getMemberQualifiedName(PsiMember member) { if (member instanceof PsiClass) { return ((PsiClass)member).getQualifiedName(); } PsiClass containingClass = member.getContainingClass(); if (containingClass == null) return null; String className = containingClass.getQualifiedName(); if (className == null) return null; return className + "." + member.getName(); } static boolean checkSameExpression(PsiExpression templateExpr, final PsiExpression expression) { return templateExpr.equals(skipParenthesizedExprDown(expression)); } public static boolean isCondition(PsiExpression expr, PsiElement parent) { if (parent instanceof PsiIfStatement) { if (checkSameExpression(expr, ((PsiIfStatement)parent).getCondition())) { return true; } } else if (parent instanceof PsiWhileStatement) { if (checkSameExpression(expr, ((PsiWhileStatement)parent).getCondition())) { return true; } } else if (parent instanceof PsiForStatement) { if (checkSameExpression(expr, ((PsiForStatement)parent).getCondition())) { return true; } } else if (parent instanceof PsiDoWhileStatement) { if (checkSameExpression(expr, ((PsiDoWhileStatement)parent).getCondition())) { return true; } } return false; } }