NPE: process usages in non-java files (17195)
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / util / RefactoringConflictsUtil.java
blob9cca54530051c6b0120af2cc6ab16dacba5af1d4
1 /*
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.
18 * User: anna
19 * Date: 05-Oct-2009
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;
46 import java.util.Set;
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,
57 String newVisibility,
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) {
71 try {
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);
131 } else {
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,
161 PsiMember member,
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,
183 PsiElement target,
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);
228 NextUsage:
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);
246 else {
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)),
260 scopeDescription,
261 CommonRefactoringUtil.htmlEmphasize(module.getName()));
263 else {
264 message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.from.module.2",
265 CommonRefactoringUtil.capitalize(
266 RefactoringUIUtil.getDescription(referencedElement, true)),
267 scopeDescription,
268 CommonRefactoringUtil.htmlEmphasize(module.getName()));
270 conflicts.putValue(referencedElement, message);