3 * Copyright 2000-2009 JetBrains s.r.o.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 package com
.intellij
.codeInsight
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.util
.Key
;
21 import com
.intellij
.psi
.*;
22 import com
.intellij
.psi
.util
.InheritanceUtil
;
23 import com
.intellij
.psi
.util
.PsiTreeUtil
;
24 import com
.intellij
.refactoring
.util
.FieldConflictsResolver
;
25 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
26 import com
.intellij
.util
.IncorrectOperationException
;
27 import org
.jetbrains
.annotations
.Nullable
;
29 public class ChangeContextUtil
{
30 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.ChangeContextUtil");
32 public static final Key
<String
> ENCODED_KEY
= Key
.create("ENCODED_KEY");
33 public static final Key
<PsiClass
> THIS_QUALIFIER_CLASS_KEY
= Key
.create("THIS_QUALIFIER_CLASS_KEY");
34 public static final Key
<PsiMember
> REF_MEMBER_KEY
= Key
.create("REF_MEMBER_KEY");
35 public static final Key
<Boolean
> CAN_REMOVE_QUALIFIER_KEY
= Key
.create("CAN_REMOVE_QUALIFIER_KEY");
36 public static final Key
<PsiClass
> REF_CLASS_KEY
= Key
.create("REF_CLASS_KEY");
37 public static final Key
<PsiClass
> REF_MEMBER_THIS_CLASS_KEY
= Key
.create("REF_MEMBER_THIS_CLASS_KEY");
39 private ChangeContextUtil() {}
41 public static void encodeContextInfo(PsiElement scope
, boolean includeRefClasses
) {
42 encodeContextInfo(scope
, scope
, includeRefClasses
);
45 private static void encodeContextInfo(PsiElement scope
, PsiElement topLevelScope
, boolean includeRefClasses
) {
46 if (scope
instanceof PsiThisExpression
){
47 scope
.putCopyableUserData(ENCODED_KEY
, "");
49 PsiThisExpression thisExpr
= (PsiThisExpression
)scope
;
50 final PsiJavaCodeReferenceElement qualifier
= thisExpr
.getQualifier();
51 if (qualifier
== null){
52 PsiClass thisClass
= RefactoringUtil
.getThisClass(thisExpr
);
53 if (thisClass
!= null && !(thisClass
instanceof PsiAnonymousClass
)){
54 thisExpr
.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY
, thisClass
);
58 final PsiElement resolved
= qualifier
.resolve();
59 if (resolved
instanceof PsiClass
&& resolved
== topLevelScope
) {
60 thisExpr
.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY
, (PsiClass
)topLevelScope
);
64 else if (scope
instanceof PsiReferenceExpression
){
65 scope
.putCopyableUserData(ENCODED_KEY
, "");
67 PsiReferenceExpression refExpr
= (PsiReferenceExpression
)scope
;
68 PsiExpression qualifier
= refExpr
.getQualifierExpression();
69 if (qualifier
== null){
70 final JavaResolveResult resolveResult
= refExpr
.advancedResolve(false);
71 final PsiElement refElement
= resolveResult
.getElement();
72 if (refElement
!= null && !PsiTreeUtil
.isAncestor(topLevelScope
, refElement
, false)){
73 if (refElement
instanceof PsiClass
){
74 if (includeRefClasses
){
75 refExpr
.putCopyableUserData(REF_CLASS_KEY
, (PsiClass
)refElement
);
78 else if (refElement
instanceof PsiMember
){
79 refExpr
.putCopyableUserData(REF_MEMBER_KEY
, ( (PsiMember
)refElement
));
80 final PsiElement resolveScope
= resolveResult
.getCurrentFileResolveScope();
81 if (resolveScope
instanceof PsiClass
&& !PsiTreeUtil
.isAncestor(topLevelScope
, resolveScope
, false)) {
82 refExpr
.putCopyableUserData(REF_MEMBER_THIS_CLASS_KEY
, (PsiClass
)resolveScope
);
88 refExpr
.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY
, canRemoveQualifier(refExpr
) ? Boolean
.TRUE
: Boolean
.FALSE
);
91 else if (includeRefClasses
) {
92 PsiReference ref
= scope
.getReference();
94 scope
.putCopyableUserData(ENCODED_KEY
, "");
96 PsiElement refElement
= ref
.resolve();
97 if (refElement
instanceof PsiClass
&& !PsiTreeUtil
.isAncestor(topLevelScope
, refElement
, false)){
98 scope
.putCopyableUserData(REF_CLASS_KEY
, (PsiClass
)refElement
);
103 for(PsiElement child
= scope
.getFirstChild(); child
!= null; child
= child
.getNextSibling()){
104 encodeContextInfo(child
, topLevelScope
, includeRefClasses
);
108 public static PsiElement
decodeContextInfo(PsiElement scope
,
110 @Nullable PsiExpression thisAccessExpr
) throws IncorrectOperationException
{
111 if (scope
.getCopyableUserData(ENCODED_KEY
) != null){
112 scope
.putCopyableUserData(ENCODED_KEY
, null);
114 if (scope
instanceof PsiThisExpression
){
115 PsiThisExpression thisExpr
= (PsiThisExpression
)scope
;
116 scope
= decodeThisExpression(thisExpr
, thisClass
, thisAccessExpr
);
118 else if (scope
instanceof PsiReferenceExpression
){
119 scope
= decodeReferenceExpression((PsiReferenceExpression
)scope
, thisAccessExpr
, thisClass
);
122 PsiClass refClass
= scope
.getCopyableUserData(REF_CLASS_KEY
);
123 scope
.putCopyableUserData(REF_CLASS_KEY
, null);
125 if (refClass
!= null && refClass
.isValid()){
126 PsiReference ref
= scope
.getReference();
128 final String qualifiedName
= refClass
.getQualifiedName();
129 if (qualifiedName
!= null){
130 if (JavaPsiFacade
.getInstance(refClass
.getProject()).findClass(qualifiedName
, scope
.getResolveScope()) != null) {
131 scope
= ref
.bindToElement(refClass
);
139 if (scope
instanceof PsiClass
){
140 if (thisAccessExpr
!= null){
141 thisAccessExpr
= (PsiExpression
)qualifyThis(thisAccessExpr
, thisClass
);
145 for(PsiElement child
= scope
.getFirstChild(); child
!= null; child
= child
.getNextSibling()){
146 child
= decodeContextInfo(child
, thisClass
, thisAccessExpr
);
152 private static PsiElement
decodeThisExpression(PsiThisExpression thisExpr
,
154 PsiExpression thisAccessExpr
) throws IncorrectOperationException
{
155 final PsiJavaCodeReferenceElement qualifier
= thisExpr
.getQualifier();
156 PsiClass encodedQualifierClass
= thisExpr
.getCopyableUserData(THIS_QUALIFIER_CLASS_KEY
);
157 thisExpr
.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY
, null);
158 if (qualifier
== null){
159 if (encodedQualifierClass
!= null && encodedQualifierClass
.isValid()){
160 if (encodedQualifierClass
.equals(thisClass
) && thisAccessExpr
!= null && thisAccessExpr
.isValid()){
161 return thisExpr
.replace(thisAccessExpr
);
166 PsiClass qualifierClass
= (PsiClass
)qualifier
.resolve();
167 if (encodedQualifierClass
== qualifierClass
&& thisClass
!= null) {
168 qualifier
.bindToElement(thisClass
);
171 if (qualifierClass
!= null) {
172 if (qualifierClass
.equals(thisClass
) && thisAccessExpr
!= null && thisAccessExpr
.isValid()) {
173 return thisExpr
.replace(thisAccessExpr
);
181 private static PsiReferenceExpression
decodeReferenceExpression(PsiReferenceExpression refExpr
,
182 PsiExpression thisAccessExpr
,
183 PsiClass thisClass
) throws IncorrectOperationException
{
184 PsiManager manager
= refExpr
.getManager();
185 PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
187 PsiExpression qualifier
= refExpr
.getQualifierExpression();
188 if (qualifier
== null){
189 PsiMember refMember
= refExpr
.getCopyableUserData(REF_MEMBER_KEY
);
190 refExpr
.putCopyableUserData(REF_MEMBER_KEY
, null);
192 if (refMember
!= null && refMember
.isValid()){
193 PsiClass containingClass
= refMember
.getContainingClass();
194 if (refMember
.hasModifierProperty(PsiModifier
.STATIC
)){
195 PsiElement refElement
= refExpr
.resolve();
196 if (!manager
.areElementsEquivalent(refMember
, refElement
)){
197 refExpr
.setQualifierExpression(factory
.createReferenceExpression(containingClass
));
201 final PsiClass realParentClass
= refExpr
.getCopyableUserData(REF_MEMBER_THIS_CLASS_KEY
);
202 refExpr
.putCopyableUserData(REF_MEMBER_THIS_CLASS_KEY
, null);
203 if (thisAccessExpr
!= null && thisClass
!= null && realParentClass
!= null &&
204 InheritanceUtil
.isInheritorOrSelf(thisClass
, realParentClass
, true)) {
205 boolean needQualifier
= true;
206 PsiElement refElement
= refExpr
.resolve();
207 if (refMember
.equals(refElement
)){
208 if (thisAccessExpr
instanceof PsiThisExpression
&& ((PsiThisExpression
)thisAccessExpr
).getQualifier() == null) {
210 needQualifier
= false;
213 final PsiClass currentClass
= findThisClass(refExpr
, refMember
);
214 if (thisAccessExpr
instanceof PsiThisExpression
){
215 PsiJavaCodeReferenceElement thisQualifier
= ((PsiThisExpression
)thisAccessExpr
).getQualifier();
216 PsiClass thisExprClass
= thisQualifier
!= null
217 ?
(PsiClass
)thisQualifier
.resolve()
218 : RefactoringUtil
.getThisClass(refExpr
);
219 if (currentClass
.equals(thisExprClass
) || thisExprClass
.isInheritor(realParentClass
, true)){ // qualifier is not necessary
220 needQualifier
= false;
227 refExpr
.setQualifierExpression(thisAccessExpr
);
230 else if (thisClass
!= null && realParentClass
!= null && PsiTreeUtil
.isAncestor(realParentClass
, thisClass
, true)) {
231 PsiElement refElement
= refExpr
.resolve();
232 if (refElement
!= null && !manager
.areElementsEquivalent(refMember
, refElement
)) {
233 refExpr
= FieldConflictsResolver
.qualifyReference(refExpr
, refMember
, null);
239 PsiClass refClass
= refExpr
.getCopyableUserData(REF_CLASS_KEY
);
240 refExpr
.putCopyableUserData(REF_CLASS_KEY
, null);
241 if (refClass
!= null && refClass
.isValid()){
242 refExpr
= (PsiReferenceExpression
)refExpr
.bindToElement(refClass
);
247 Boolean couldRemove
= refExpr
.getCopyableUserData(CAN_REMOVE_QUALIFIER_KEY
);
248 refExpr
.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY
, null);
250 if (couldRemove
== Boolean
.FALSE
&& canRemoveQualifier(refExpr
)){
251 PsiReferenceExpression newRefExpr
= (PsiReferenceExpression
)factory
.createExpressionFromText(
252 refExpr
.getReferenceName(), null);
253 refExpr
= (PsiReferenceExpression
)refExpr
.replace(newRefExpr
);
259 private static PsiClass
findThisClass(PsiReferenceExpression refExpr
, PsiMember refMember
) {
260 LOG
.assertTrue(refExpr
.getQualifierExpression() == null);
261 final PsiClass refMemberClass
= refMember
.getContainingClass();
262 if (refMemberClass
== null) return null;
263 PsiElement parent
= refExpr
.getContext();
264 while(parent
!= null){
265 if (parent
instanceof PsiClass
){
266 if (parent
.equals(refMemberClass
) || ((PsiClass
)parent
).isInheritor(refMemberClass
, true)){
267 return (PsiClass
)parent
;
270 parent
= parent
.getContext();
273 return refMemberClass
;
276 private static boolean canRemoveQualifier(PsiReferenceExpression refExpr
) {
278 PsiExpression qualifier
= refExpr
.getQualifierExpression();
279 if (!(qualifier
instanceof PsiReferenceExpression
)) return false;
280 PsiElement qualifierRefElement
= ((PsiReferenceExpression
)qualifier
).resolve();
281 if (!(qualifierRefElement
instanceof PsiClass
)) return false;
282 PsiElement refElement
= refExpr
.resolve();
283 if (refElement
== null) return false;
284 PsiElementFactory factory
= JavaPsiFacade
.getInstance(refExpr
.getProject()).getElementFactory();
285 if (refExpr
.getParent() instanceof PsiMethodCallExpression
){
286 PsiMethodCallExpression methodCall
= (PsiMethodCallExpression
)refExpr
.getParent();
287 PsiMethodCallExpression newMethodCall
= (PsiMethodCallExpression
)factory
.createExpressionFromText(
288 refExpr
.getReferenceName() + "()", refExpr
);
289 newMethodCall
.getArgumentList().replace(methodCall
.getArgumentList());
290 PsiElement newRefElement
= newMethodCall
.getMethodExpression().resolve();
291 return refElement
.equals(newRefElement
);
294 PsiReferenceExpression newRefExpr
= (PsiReferenceExpression
)factory
.createExpressionFromText(
295 refExpr
.getReferenceName(), refExpr
);
296 PsiElement newRefElement
= newRefExpr
.resolve();
297 return refElement
.equals(newRefElement
);
300 catch(IncorrectOperationException e
){
306 private static PsiElement
qualifyThis(PsiElement scope
, PsiClass thisClass
) throws IncorrectOperationException
{
307 if (scope
instanceof PsiThisExpression
){
308 PsiThisExpression thisExpr
= (PsiThisExpression
)scope
;
309 if (thisExpr
.getQualifier() == null){
310 if (thisClass
instanceof PsiAnonymousClass
) return null;
311 PsiThisExpression qualifiedThis
= RefactoringUtil
.createThisExpression(thisClass
.getManager(), thisClass
);
312 if (thisExpr
.getParent() != null) {
313 return thisExpr
.replace(qualifiedThis
);
315 return qualifiedThis
;
319 else if (!(scope
instanceof PsiClass
)){
320 for(PsiElement child
= scope
.getFirstChild(); child
!= null; child
= child
.getNextSibling()){
321 if (qualifyThis(child
, thisClass
) == null) return null;
327 public static PsiClass
getThisClass(PsiElement element
) {
328 return PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
331 public static void clearContextInfo(PsiElement scope
) {
332 scope
.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY
, null);
333 scope
.putCopyableUserData(REF_MEMBER_KEY
, null);
334 scope
.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY
, null);
335 for(PsiElement child
= scope
.getFirstChild(); child
!= null; child
= child
.getNextSibling()){
336 clearContextInfo(child
);