diff options
author | Tor Norbye <tnorbye@google.com> | 2013-05-08 15:46:07 -0700 |
---|---|---|
committer | Tor Norbye <tnorbye@google.com> | 2013-05-08 15:46:07 -0700 |
commit | a6eac331b3d9f0d4168b12356ea256c83f4e9c05 (patch) | |
tree | 923ceb497c43ea183351321bb4b9e388851a7854 /java/java-psi-impl/src/com/intellij/codeInsight/ChangeContextUtil.java | |
parent | 934b9431b0b827a132df794e307fe5a2b70de00b (diff) | |
download | idea-a6eac331b3d9f0d4168b12356ea256c83f4e9c05.tar.gz |
Snapshot f5ae6e3be7e12e1ef9e12f48fe3a674266288e4e from master branch of git://git.jetbrains.org/idea/community.git
Change-Id: I756af70fb2910aa2687e94e28338fb9727bce518
Diffstat (limited to 'java/java-psi-impl/src/com/intellij/codeInsight/ChangeContextUtil.java')
-rw-r--r-- | java/java-psi-impl/src/com/intellij/codeInsight/ChangeContextUtil.java | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/ChangeContextUtil.java b/java/java-psi-impl/src/com/intellij/codeInsight/ChangeContextUtil.java new file mode 100644 index 000000000000..b93057fd060b --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/codeInsight/ChangeContextUtil.java @@ -0,0 +1,352 @@ +/* + * Copyright 2000-2009 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.codeInsight; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Key; +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.util.RefactoringChangeUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ChangeContextUtil { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ChangeContextUtil"); + + public static final Key<String> ENCODED_KEY = Key.create("ENCODED_KEY"); + public static final Key<PsiClass> THIS_QUALIFIER_CLASS_KEY = Key.create("THIS_QUALIFIER_CLASS_KEY"); + public static final Key<PsiMember> REF_MEMBER_KEY = Key.create("REF_MEMBER_KEY"); + public static final Key<Boolean> CAN_REMOVE_QUALIFIER_KEY = Key.create("CAN_REMOVE_QUALIFIER_KEY"); + public static final Key<PsiClass> REF_CLASS_KEY = Key.create("REF_CLASS_KEY"); + public static final Key<PsiClass> REF_MEMBER_THIS_CLASS_KEY = Key.create("REF_MEMBER_THIS_CLASS_KEY"); + + private ChangeContextUtil() {} + + public static void encodeContextInfo(PsiElement scope, boolean includeRefClasses) { + encodeContextInfo(scope, scope, includeRefClasses, true); + } + + public static void encodeContextInfo(PsiElement scope, boolean includeRefClasses, boolean canChangeQualifier) { + encodeContextInfo(scope, scope, includeRefClasses, canChangeQualifier); + } + + private static void encodeContextInfo(PsiElement scope, + PsiElement topLevelScope, + boolean includeRefClasses, + boolean canChangeQualifier) { + if (scope instanceof PsiThisExpression){ + scope.putCopyableUserData(ENCODED_KEY, ""); + + PsiThisExpression thisExpr = (PsiThisExpression)scope; + final PsiJavaCodeReferenceElement qualifier = thisExpr.getQualifier(); + if (qualifier == null){ + PsiClass thisClass = RefactoringChangeUtil.getThisClass(thisExpr); + if (thisClass != null && !(thisClass instanceof PsiAnonymousClass)){ + thisExpr.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, thisClass); + } + } + else { + final PsiElement resolved = qualifier.resolve(); + if (resolved instanceof PsiClass && resolved == topLevelScope) { + thisExpr.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, (PsiClass)topLevelScope); + } + } + } + else if (scope instanceof PsiReferenceExpression){ + scope.putCopyableUserData(ENCODED_KEY, ""); + + PsiReferenceExpression refExpr = (PsiReferenceExpression)scope; + PsiExpression qualifier = refExpr.getQualifierExpression(); + if (qualifier == null){ + final JavaResolveResult resolveResult = refExpr.advancedResolve(false); + final PsiElement refElement = resolveResult.getElement(); + if (refElement != null && !PsiTreeUtil.isAncestor(topLevelScope, refElement, false)){ + if (refElement instanceof PsiClass){ + if (includeRefClasses){ + refExpr.putCopyableUserData(REF_CLASS_KEY, (PsiClass)refElement); + } + } + else if (refElement instanceof PsiMember){ + refExpr.putCopyableUserData(REF_MEMBER_KEY, ( (PsiMember)refElement)); + final PsiElement resolveScope = resolveResult.getCurrentFileResolveScope(); + if (resolveScope instanceof PsiClass && !PsiTreeUtil.isAncestor(topLevelScope, resolveScope, false)) { + refExpr.putCopyableUserData(REF_MEMBER_THIS_CLASS_KEY, (PsiClass)resolveScope); + } + } + } + } + else if (canChangeQualifier) { + refExpr.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, canRemoveQualifier(refExpr) ? Boolean.TRUE : Boolean.FALSE); + } + } + else if (includeRefClasses) { + PsiReference ref = scope.getReference(); + if (ref != null){ + scope.putCopyableUserData(ENCODED_KEY, ""); + + PsiElement refElement = ref.resolve(); + if (refElement instanceof PsiClass && !PsiTreeUtil.isAncestor(topLevelScope, refElement, false)){ + scope.putCopyableUserData(REF_CLASS_KEY, (PsiClass)refElement); + } + } + } + + for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){ + encodeContextInfo(child, topLevelScope, includeRefClasses, canChangeQualifier); + } + } + + public static PsiElement decodeContextInfo(@NotNull PsiElement scope, + @Nullable PsiClass thisClass, + @Nullable PsiExpression thisAccessExpr) throws IncorrectOperationException { + if (scope.getCopyableUserData(ENCODED_KEY) != null) { + scope.putCopyableUserData(ENCODED_KEY, null); + + if (scope instanceof PsiThisExpression) { + PsiThisExpression thisExpr = (PsiThisExpression)scope; + scope = decodeThisExpression(thisExpr, thisClass, thisAccessExpr); + } + else if (scope instanceof PsiReferenceExpression) { + scope = decodeReferenceExpression((PsiReferenceExpression)scope, thisAccessExpr, thisClass); + } + else { + PsiClass refClass = scope.getCopyableUserData(REF_CLASS_KEY); + scope.putCopyableUserData(REF_CLASS_KEY, null); + + if (refClass != null && refClass.isValid()) { + PsiReference ref = scope.getReference(); + if (ref != null) { + final String qualifiedName = refClass.getQualifiedName(); + if (qualifiedName != null) { + if (JavaPsiFacade.getInstance(refClass.getProject()).findClass(qualifiedName, scope.getResolveScope()) != null) { + scope = ref.bindToElement(refClass); + } + } + } + } + } + } + + if (scope instanceof PsiClass) { + if (thisAccessExpr != null) { + thisAccessExpr = (PsiExpression)qualifyThis(thisAccessExpr, thisClass); + } + } + + PsiElement child = scope.getFirstChild(); + while (child != null) { + child = decodeContextInfo(child, thisClass, thisAccessExpr).getNextSibling(); + } + + return scope; + } + + private static PsiElement decodeThisExpression(PsiThisExpression thisExpr, + PsiClass thisClass, + PsiExpression thisAccessExpr) throws IncorrectOperationException { + final PsiJavaCodeReferenceElement qualifier = thisExpr.getQualifier(); + PsiClass encodedQualifierClass = thisExpr.getCopyableUserData(THIS_QUALIFIER_CLASS_KEY); + thisExpr.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, null); + if (qualifier == null){ + if (encodedQualifierClass != null && encodedQualifierClass.isValid()){ + if (encodedQualifierClass.equals(thisClass) && thisAccessExpr != null && thisAccessExpr.isValid()){ + return thisExpr.replace(thisAccessExpr); + } + } + } + else { + PsiClass qualifierClass = (PsiClass)qualifier.resolve(); + if (encodedQualifierClass == qualifierClass && thisClass != null) { + qualifier.bindToElement(thisClass); + } + else { + if (qualifierClass != null) { + if (qualifierClass.equals(thisClass) && thisAccessExpr != null && thisAccessExpr.isValid()) { + return thisExpr.replace(thisAccessExpr); + } + } + } + } + return thisExpr; + } + + private static PsiReferenceExpression decodeReferenceExpression(@NotNull PsiReferenceExpression refExpr, + PsiExpression thisAccessExpr, + PsiClass thisClass) throws IncorrectOperationException { + PsiManager manager = refExpr.getManager(); + PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); + + PsiExpression qualifier = refExpr.getQualifierExpression(); + if (qualifier == null){ + PsiMember refMember = refExpr.getCopyableUserData(REF_MEMBER_KEY); + refExpr.putCopyableUserData(REF_MEMBER_KEY, null); + + if (refMember != null && refMember.isValid()){ + PsiClass containingClass = refMember.getContainingClass(); + if (refMember.hasModifierProperty(PsiModifier.STATIC)){ + PsiElement refElement = refExpr.resolve(); + if (!manager.areElementsEquivalent(refMember, refElement)){ + refExpr.setQualifierExpression(factory.createReferenceExpression(containingClass)); + } + } + else { + final PsiClass realParentClass = refExpr.getCopyableUserData(REF_MEMBER_THIS_CLASS_KEY); + refExpr.putCopyableUserData(REF_MEMBER_THIS_CLASS_KEY, null); + if (thisAccessExpr != null && thisClass != null && realParentClass != null && + InheritanceUtil.isInheritorOrSelf(thisClass, realParentClass, true)) { + boolean needQualifier = true; + PsiElement refElement = refExpr.resolve(); + if (refMember.equals(refElement) || + (refElement instanceof PsiMethod && refMember instanceof PsiMethod && ArrayUtil.find(((PsiMethod)refElement).findSuperMethods(), refMember) > -1)){ + if (thisAccessExpr instanceof PsiThisExpression && ((PsiThisExpression)thisAccessExpr).getQualifier() == null) { + //Trivial qualifier + needQualifier = false; + } + else { + final PsiClass currentClass = findThisClass(refExpr, refMember); + if (thisAccessExpr instanceof PsiThisExpression){ + PsiJavaCodeReferenceElement thisQualifier = ((PsiThisExpression)thisAccessExpr).getQualifier(); + PsiClass thisExprClass = thisQualifier != null + ? (PsiClass)thisQualifier.resolve() + : RefactoringChangeUtil.getThisClass(refExpr); + if (currentClass.equals(thisExprClass) || thisExprClass.isInheritor(realParentClass, true)){ // qualifier is not necessary + needQualifier = false; + } + } + } + } + + if (needQualifier){ + refExpr.setQualifierExpression(thisAccessExpr); + } + } + else if (thisClass != null && realParentClass != null && PsiTreeUtil.isAncestor(realParentClass, thisClass, true)) { + PsiElement refElement = refExpr.resolve(); + if (refElement != null && !manager.areElementsEquivalent(refMember, refElement)) { + refExpr = RefactoringChangeUtil.qualifyReference(refExpr, refMember, null); + } + } + } + } + else { + PsiClass refClass = refExpr.getCopyableUserData(REF_CLASS_KEY); + refExpr.putCopyableUserData(REF_CLASS_KEY, null); + if (refClass != null && refClass.isValid()){ + refExpr = (PsiReferenceExpression)refExpr.bindToElement(refClass); + } + } + } + else{ + Boolean couldRemove = refExpr.getCopyableUserData(CAN_REMOVE_QUALIFIER_KEY); + refExpr.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, null); + + if (couldRemove == Boolean.FALSE && canRemoveQualifier(refExpr)){ + PsiReferenceExpression newRefExpr = (PsiReferenceExpression)factory.createExpressionFromText( + refExpr.getReferenceName(), null); + refExpr = (PsiReferenceExpression)refExpr.replace(newRefExpr); + } + } + return refExpr; + } + + private static PsiClass findThisClass(PsiReferenceExpression refExpr, PsiMember refMember) { + LOG.assertTrue(refExpr.getQualifierExpression() == null); + final PsiClass refMemberClass = refMember.getContainingClass(); + if (refMemberClass == null) return null; + PsiElement parent = refExpr.getContext(); + while(parent != null){ + if (parent instanceof PsiClass){ + if (parent.equals(refMemberClass) || ((PsiClass)parent).isInheritor(refMemberClass, true)){ + return (PsiClass)parent; + } + } + parent = parent.getContext(); + } + + return refMemberClass; + } + + public static boolean canRemoveQualifier(PsiReferenceExpression refExpr) { + try{ + PsiExpression qualifier = refExpr.getQualifierExpression(); + if (!(qualifier instanceof PsiReferenceExpression)) return false; + if (refExpr.getTypeParameters().length > 0) return false; + PsiElement qualifierRefElement = ((PsiReferenceExpression)qualifier).resolve(); + if (!(qualifierRefElement instanceof PsiClass)) return false; + PsiElement refElement = refExpr.resolve(); + if (refElement == null) return false; + PsiElementFactory factory = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory(); + if (refExpr.getParent() instanceof PsiMethodCallExpression){ + PsiMethodCallExpression methodCall = (PsiMethodCallExpression)refExpr.getParent(); + PsiMethodCallExpression newMethodCall = (PsiMethodCallExpression)factory.createExpressionFromText( + refExpr.getReferenceName() + "()", refExpr); + newMethodCall.getArgumentList().replace(methodCall.getArgumentList()); + PsiElement newRefElement = newMethodCall.getMethodExpression().resolve(); + return refElement.equals(newRefElement); + } + else if (refExpr instanceof PsiMethodReferenceExpression) { + return false; + } + else { + PsiReferenceExpression newRefExpr = (PsiReferenceExpression)factory.createExpressionFromText( + refExpr.getReferenceName(), refExpr); + PsiElement newRefElement = newRefExpr.resolve(); + return refElement.equals(newRefElement); + } + } + catch(IncorrectOperationException e){ + LOG.error(e); + return false; + } + } + + private static PsiElement qualifyThis(PsiElement scope, PsiClass thisClass) throws IncorrectOperationException { + if (scope instanceof PsiThisExpression){ + PsiThisExpression thisExpr = (PsiThisExpression)scope; + if (thisExpr.getQualifier() == null){ + if (thisClass instanceof PsiAnonymousClass) return null; + PsiThisExpression qualifiedThis = RefactoringChangeUtil.createThisExpression(thisClass.getManager(), thisClass); + if (thisExpr.getParent() != null) { + return thisExpr.replace(qualifiedThis); + } else { + return qualifiedThis; + } + } + } + else if (!(scope instanceof PsiClass)){ + for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){ + if (qualifyThis(child, thisClass) == null) return null; + } + } + return scope; + } + + public static PsiClass getThisClass(PsiElement element) { + return PsiTreeUtil.getParentOfType(element, PsiClass.class); + } + + public static void clearContextInfo(PsiElement scope) { + scope.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, null); + scope.putCopyableUserData(REF_MEMBER_KEY, null); + scope.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, null); + for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){ + clearContextInfo(child); + } + } +} |