rename: remember 'Search for text occurrences' checkbox state (IDEA-21328)
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / ChangeContextUtil.java
blob36984b30e4874d4ecc365be745f2519470cdaef8
2 /*
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);
57 else {
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);
87 else{
88 refExpr.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, canRemoveQualifier(refExpr) ? Boolean.TRUE : Boolean.FALSE);
91 else if (includeRefClasses) {
92 PsiReference ref = scope.getReference();
93 if (ref != null){
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,
109 PsiClass thisClass,
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);
121 else {
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();
127 if (ref != null) {
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);
149 return scope;
152 private static PsiElement decodeThisExpression(PsiThisExpression thisExpr,
153 PsiClass thisClass,
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);
165 else {
166 PsiClass qualifierClass = (PsiClass)qualifier.resolve();
167 if (encodedQualifierClass == qualifierClass && thisClass != null) {
168 qualifier.bindToElement(thisClass);
170 else {
171 if (qualifierClass != null) {
172 if (qualifierClass.equals(thisClass) && thisAccessExpr != null && thisAccessExpr.isValid()) {
173 return thisExpr.replace(thisAccessExpr);
178 return thisExpr;
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));
200 else {
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) {
209 //Trivial qualifier
210 needQualifier = false;
212 else {
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;
226 if (needQualifier){
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);
238 else {
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);
246 else{
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);
256 return refExpr;
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) {
277 try{
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);
293 else{
294 PsiReferenceExpression newRefExpr = (PsiReferenceExpression)factory.createExpressionFromText(
295 refExpr.getReferenceName(), refExpr);
296 PsiElement newRefElement = newRefExpr.resolve();
297 return refElement.equals(newRefElement);
300 catch(IncorrectOperationException e){
301 LOG.error(e);
302 return false;
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);
314 } else {
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;
324 return scope;
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);