2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
21 package com
.intellij
.refactoring
.util
;
23 import com
.intellij
.openapi
.module
.Module
;
24 import com
.intellij
.openapi
.module
.ModuleUtil
;
25 import com
.intellij
.openapi
.project
.Project
;
26 import com
.intellij
.openapi
.roots
.ModuleRootManager
;
27 import com
.intellij
.openapi
.roots
.ProjectRootManager
;
28 import com
.intellij
.openapi
.vfs
.VirtualFile
;
29 import com
.intellij
.psi
.*;
30 import com
.intellij
.psi
.search
.GlobalSearchScope
;
31 import com
.intellij
.psi
.search
.PsiSearchScopeUtil
;
32 import com
.intellij
.psi
.search
.searches
.ReferencesSearch
;
33 import com
.intellij
.psi
.util
.PsiTreeUtil
;
34 import com
.intellij
.psi
.util
.PsiUtil
;
35 import com
.intellij
.psi
.util
.PsiUtilBase
;
36 import com
.intellij
.refactoring
.RefactoringBundle
;
37 import com
.intellij
.usageView
.UsageInfo
;
38 import com
.intellij
.util
.IncorrectOperationException
;
39 import com
.intellij
.util
.VisibilityUtil
;
40 import com
.intellij
.util
.containers
.HashSet
;
41 import com
.intellij
.util
.containers
.MultiMap
;
42 import org
.jetbrains
.annotations
.NotNull
;
43 import org
.jetbrains
.annotations
.Nullable
;
45 import java
.util
.Collection
;
48 public class RefactoringConflictsUtil
{
50 public static void analyzeAccessibilityConflicts(@NotNull Set
<PsiMember
> membersToMove
,
51 @NotNull final PsiClass targetClass
,
52 final MultiMap
<PsiElement
, String
> conflicts
, String newVisibility
) {
53 analyzeAccessibilityConflicts(membersToMove
, targetClass
, conflicts
, newVisibility
, targetClass
, null);
56 public static void analyzeAccessibilityConflicts(@NotNull Set
<PsiMember
> membersToMove
, @Nullable final PsiClass targetClass
, final MultiMap
<PsiElement
, String
> conflicts
,
58 @NotNull PsiElement context
,
59 @Nullable Set
<PsiMethod
> abstractMethods
) {
60 if (VisibilityUtil
.ESCALATE_VISIBILITY
.equals(newVisibility
)) { //Still need to check for access object
61 newVisibility
= PsiModifier
.PUBLIC
;
64 for (PsiMember member
: membersToMove
) {
65 checkUsedElements(member
, member
, membersToMove
, abstractMethods
, targetClass
, context
, conflicts
);
67 PsiModifierList modifierList
= member
.getModifierList();
68 if (modifierList
!=null) modifierList
= (PsiModifierList
)modifierList
.copy();
70 if (newVisibility
!= null) {
72 if (modifierList
!=null) VisibilityUtil
.setVisibility(modifierList
, newVisibility
);
74 catch (IncorrectOperationException ex
) {
75 /* do nothing and hope for the best */
78 JavaPsiFacade manager
= JavaPsiFacade
.getInstance(member
.getProject());
79 for (PsiReference psiReference
: ReferencesSearch
.search(member
)) {
80 PsiElement ref
= psiReference
.getElement();
81 if (!RefactoringHierarchyUtil
.willBeInTargetClass(ref
, membersToMove
, targetClass
, false)) {
82 //Check for target class accessibility
83 if (targetClass
!= null && !manager
.getResolveHelper().isAccessible(targetClass
, targetClass
.getModifierList(), ref
, null, null)) {
84 String message
= RefactoringBundle
.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
85 RefactoringUIUtil
.getDescription(targetClass
, true),
86 VisibilityUtil
.getVisibilityStringToDisplay(targetClass
),
87 RefactoringUIUtil
.getDescription(ConflictsUtil
.getContainer(ref
), true));
88 message
= CommonRefactoringUtil
.capitalize(message
);
89 conflicts
.putValue(targetClass
, message
);
91 //check for member accessibility
92 else if (!manager
.getResolveHelper().isAccessible(member
, modifierList
, ref
, null, null)) {
93 String message
= RefactoringBundle
.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
94 RefactoringUIUtil
.getDescription(member
, true),
95 VisibilityUtil
.getVisibilityStringToDisplay(member
),
96 RefactoringUIUtil
.getDescription(ConflictsUtil
.getContainer(ref
), true));
97 message
= CommonRefactoringUtil
.capitalize(message
);
98 conflicts
.putValue(member
, message
);
105 public static void checkUsedElements(PsiMember member
, PsiElement scope
, @NotNull Set
<PsiMember
> membersToMove
,
106 @Nullable Set
<PsiMethod
> abstractMethods
, @Nullable PsiClass targetClass
,
107 @NotNull PsiElement context
,
108 MultiMap
<PsiElement
, String
> conflicts
) {
109 final Set
<PsiMember
> moving
= new HashSet
<PsiMember
>(membersToMove
);
110 if (abstractMethods
!= null) {
111 moving
.addAll(abstractMethods
);
113 if(scope
instanceof PsiReferenceExpression
) {
114 PsiReferenceExpression refExpr
= (PsiReferenceExpression
)scope
;
115 PsiElement refElement
= refExpr
.resolve();
116 if (refElement
instanceof PsiMember
) {
117 if (!RefactoringHierarchyUtil
.willBeInTargetClass(refElement
, moving
, targetClass
, false)){
118 PsiExpression qualifier
= refExpr
.getQualifierExpression();
119 PsiClass accessClass
= (PsiClass
)(qualifier
!= null ? PsiUtil
.getAccessObjectClass(qualifier
).getElement() : null);
120 checkAccessibility((PsiMember
)refElement
, context
, accessClass
, member
, conflicts
);
124 else if (scope
instanceof PsiNewExpression
) {
125 final PsiNewExpression newExpression
= (PsiNewExpression
)scope
;
126 final PsiAnonymousClass anonymousClass
= newExpression
.getAnonymousClass();
127 if (anonymousClass
!= null) {
128 if (!RefactoringHierarchyUtil
.willBeInTargetClass(anonymousClass
, moving
, targetClass
, false)){
129 checkAccessibility(anonymousClass
, context
, anonymousClass
, member
, conflicts
);
132 final PsiMethod refElement
= newExpression
.resolveConstructor();
133 if (refElement
!= null) {
134 if (!RefactoringHierarchyUtil
.willBeInTargetClass(refElement
, moving
, targetClass
, false)) {
135 checkAccessibility(refElement
, context
, null, member
, conflicts
);
140 else if (scope
instanceof PsiJavaCodeReferenceElement
) {
141 PsiJavaCodeReferenceElement refExpr
= (PsiJavaCodeReferenceElement
)scope
;
142 PsiElement refElement
= refExpr
.resolve();
143 if (refElement
instanceof PsiMember
) {
144 if (!RefactoringHierarchyUtil
.willBeInTargetClass(refElement
, moving
, targetClass
, false)){
145 checkAccessibility((PsiMember
)refElement
, context
, null, member
, conflicts
);
150 PsiElement
[] children
= scope
.getChildren();
151 for (PsiElement child
: children
) {
152 if (!(child
instanceof PsiWhiteSpace
)) {
153 checkUsedElements(member
, child
, membersToMove
, abstractMethods
, targetClass
, context
, conflicts
);
158 public static void checkAccessibility(PsiMember refMember
,
159 @NotNull PsiElement newContext
,
160 PsiClass accessClass
,
162 MultiMap
<PsiElement
, String
> conflicts
) {
163 if (!PsiUtil
.isAccessible(refMember
, newContext
, accessClass
)) {
164 String message
= RefactoringBundle
.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
165 RefactoringUIUtil
.getDescription(refMember
, true),
166 VisibilityUtil
.getVisibilityStringToDisplay(refMember
),
167 RefactoringUIUtil
.getDescription(member
, false));
168 message
= CommonRefactoringUtil
.capitalize(message
);
169 conflicts
.putValue(refMember
, message
);
170 } else if (newContext
instanceof PsiClass
&& refMember
instanceof PsiField
&& refMember
.getContainingClass() == member
.getContainingClass()) {
171 final PsiField fieldInSubClass
= ((PsiClass
)newContext
).findFieldByName(refMember
.getName(), false);
172 if (fieldInSubClass
!= null) {
173 conflicts
.putValue(refMember
, CommonRefactoringUtil
.capitalize(RefactoringUIUtil
.getDescription(fieldInSubClass
, true) +
174 " would hide " + RefactoringUIUtil
.getDescription(refMember
, true) +
175 " which is used by moved " + RefactoringUIUtil
.getDescription(member
, false)));
180 public static void analyzeModuleConflicts(Project project
,
181 Collection
<?
extends PsiElement
> scope
,
182 final UsageInfo
[] usages
,
184 final MultiMap
<PsiElement
,String
> conflicts
) {
185 if (scope
== null) return;
186 final VirtualFile vFile
= PsiUtilBase
.getVirtualFile(target
);
187 if (vFile
== null) return;
188 analyzeModuleConflicts(project
, scope
, usages
, vFile
, conflicts
);
191 public static void analyzeModuleConflicts(Project project
,
192 final Collection
<?
extends PsiElement
> scopes
,
193 final UsageInfo
[] usages
,
194 final VirtualFile vFile
,
195 final MultiMap
<PsiElement
, String
> conflicts
) {
196 if (scopes
== null) return;
198 for (final PsiElement scope
: scopes
) {
199 if (scope
instanceof PsiPackage
|| scope
instanceof PsiDirectory
) return;
202 final Module targetModule
= ModuleUtil
.findModuleForFile(vFile
, project
);
203 if (targetModule
== null) return;
204 final GlobalSearchScope resolveScope
= GlobalSearchScope
.moduleWithDependenciesAndLibrariesScope(targetModule
);
205 final HashSet
<PsiElement
> reported
= new HashSet
<PsiElement
>();
206 for (final PsiElement scope
: scopes
) {
207 scope
.accept(new JavaRecursiveElementVisitor() {
208 @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference
) {
209 super.visitReferenceElement(reference
);
210 final PsiElement resolved
= reference
.resolve();
211 if (resolved
!= null && !reported
.contains(resolved
) && !CommonRefactoringUtil
.isAncestor(resolved
, scopes
) &&
212 !PsiSearchScopeUtil
.isInScope(resolveScope
, resolved
)) {
213 final String scopeDescription
=
214 RefactoringUIUtil
.getDescription(ConflictsUtil
.getContainer(reference
), true);
215 final String message
= RefactoringBundle
.message("0.referenced.in.1.will.not.be.accessible.in.module.2",
216 CommonRefactoringUtil
.capitalize(
217 RefactoringUIUtil
.getDescription(resolved
, true)), scopeDescription
,
218 CommonRefactoringUtil
.htmlEmphasize(
219 targetModule
.getName()));
220 conflicts
.putValue(resolved
, message
);
221 reported
.add(resolved
);
227 boolean isInTestSources
= ModuleRootManager
.getInstance(targetModule
).getFileIndex().isInTestSourceContent(vFile
);
229 for (UsageInfo usage
: usages
) {
230 if (usage
instanceof MoveRenameUsageInfo
) {
231 final MoveRenameUsageInfo moveRenameUsageInfo
= (MoveRenameUsageInfo
)usage
;
232 final PsiElement element
= usage
.getElement();
233 if (element
!= null && PsiTreeUtil
.getParentOfType(element
, PsiImportStatement
.class, false) == null) {
235 for (PsiElement scope
: scopes
) {
236 if (PsiTreeUtil
.isAncestor(scope
, element
, false)) continue NextUsage
;
239 final GlobalSearchScope resolveScope1
= element
.getResolveScope();
240 if (!resolveScope1
.isSearchInModuleContent(targetModule
, isInTestSources
)) {
241 final PsiFile usageFile
= element
.getContainingFile();
242 PsiElement container
;
243 if (usageFile
instanceof PsiJavaFile
) {
244 container
= ConflictsUtil
.getContainer(element
);
247 container
= usageFile
;
249 final String scopeDescription
= RefactoringUIUtil
.getDescription(container
, true);
250 final VirtualFile usageVFile
= usageFile
.getVirtualFile();
251 if (usageVFile
!= null) {
252 Module module
= ProjectRootManager
.getInstance(project
).getFileIndex().getModuleForFile(usageVFile
);
253 if (module
!= null) {
254 final String message
;
255 final PsiElement referencedElement
= moveRenameUsageInfo
.getReferencedElement();
256 if (module
== targetModule
&& isInTestSources
) {
257 message
= RefactoringBundle
.message("0.referenced.in.1.will.not.be.accessible.from.production.of.module.2",
258 CommonRefactoringUtil
.capitalize(
259 RefactoringUIUtil
.getDescription(referencedElement
, true)),
261 CommonRefactoringUtil
.htmlEmphasize(module
.getName()));
264 message
= RefactoringBundle
.message("0.referenced.in.1.will.not.be.accessible.from.module.2",
265 CommonRefactoringUtil
.capitalize(
266 RefactoringUIUtil
.getDescription(referencedElement
, true)),
268 CommonRefactoringUtil
.htmlEmphasize(module
.getName()));
270 conflicts
.putValue(referencedElement
, message
);