diff options
author | Jean-Baptiste Queru <jbq@google.com> | 2013-01-08 11:11:20 -0800 |
---|---|---|
committer | Jean-Baptiste Queru <jbq@google.com> | 2013-01-08 11:11:20 -0800 |
commit | b56ea2a18f232d79481e778085fd64e8ae486fc3 (patch) | |
tree | 44e1f6eb4864a45033f865b74fe783e3d784dd6a /java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java | |
download | idea-b56ea2a18f232d79481e778085fd64e8ae486fc3.tar.gz |
Snapshot of commit d5ec1d5018ed24f1b4f32b1d09df6dbd7e2fc425
from branch master of git://git.jetbrains.org/idea/community.git
Diffstat (limited to 'java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java')
-rw-r--r-- | java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java | 988 |
1 files changed, 988 insertions, 0 deletions
diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java new file mode 100644 index 000000000000..0585ec3b0496 --- /dev/null +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -0,0 +1,988 @@ +/* + * Copyright 2000-2012 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.util.IncorrectOperationException; +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 JavaPsiFacade.getInstance(place.getProject()).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) { + final PsiParameter[] parms = method.getParameterList().getParameters(); + if (args.length < parms.length - 1) return ApplicabilityLevel.NOT_APPLICABLE; + + if (!areFirstArgumentsApplicable(args, parms, languageLevel, substitutorForMethod)) 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)) return ApplicabilityLevel.FIXED_ARITY; + if (isRawSubstitutor(method, substitutorForMethod)) { + 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 (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) { + 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 (!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) { + String modifier = PsiModifier.PUBLIC; + + if (aClass.hasModifierProperty(PsiModifier.ABSTRACT) && !aClass.isEnum()) { + modifier = PsiModifier.PROTECTED; + } + else if (aClass.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { + modifier = PsiModifier.PACKAGE_LOCAL; + } + else if (aClass.hasModifierProperty(PsiModifier.PRIVATE)) { + modifier = PsiModifier.PRIVATE; + } + else if (aClass.isEnum()) { + 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); + final PsiFile file = element.getContainingFile(); + if (file == null) { + final LanguageLevelProjectExtension instance = LanguageLevelProjectExtension.getInstance(element.getProject()); + return instance != null ? instance.getLanguageLevel() : LanguageLevel.HIGHEST; + } + + if (!(file instanceof PsiJavaFile)) { + final PsiElement context = file.getContext(); + if (context != null) return getLanguageLevel(context); + final LanguageLevelProjectExtension instance = LanguageLevelProjectExtension.getInstance(file.getProject()); + return instance != null ? instance.getLanguageLevel() : LanguageLevel.HIGHEST; + } + + return ((PsiJavaFile)file).getLanguageLevel(); + } + + 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 final PsiElement element, final IElementType type) { + return element instanceof PsiJavaToken && ((PsiJavaToken)element).getTokenType() == type; + } + + 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; + } +} |