Move Member conflict analysis delegated into handler

This commit is contained in:
Roman Shevchenko
2011-04-14 20:19:29 +02:00
parent b1a50f79fa
commit f85c93ff06
6 changed files with 337 additions and 280 deletions
@@ -1,4 +1,3 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
@@ -24,6 +23,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.util.FieldConflictsResolver;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class ChangeContextUtil {
@@ -112,28 +112,28 @@ public class ChangeContextUtil {
}
}
public static PsiElement decodeContextInfo(PsiElement scope,
PsiClass thisClass,
public static PsiElement decodeContextInfo(@NotNull PsiElement scope,
@Nullable PsiClass thisClass,
@Nullable PsiExpression thisAccessExpr) throws IncorrectOperationException {
if (scope.getCopyableUserData(ENCODED_KEY) != null){
if (scope.getCopyableUserData(ENCODED_KEY) != null) {
scope.putCopyableUserData(ENCODED_KEY, null);
if (scope instanceof PsiThisExpression){
if (scope instanceof PsiThisExpression) {
PsiThisExpression thisExpr = (PsiThisExpression)scope;
scope = decodeThisExpression(thisExpr, thisClass, thisAccessExpr);
}
else if (scope instanceof PsiReferenceExpression){
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()){
if (refClass != null && refClass.isValid()) {
PsiReference ref = scope.getReference();
if (ref != null) {
final String qualifiedName = refClass.getQualifiedName();
if (qualifiedName != null){
if (qualifiedName != null) {
if (JavaPsiFacade.getInstance(refClass.getProject()).findClass(qualifiedName, scope.getResolveScope()) != null) {
scope = ref.bindToElement(refClass);
}
@@ -143,14 +143,15 @@ public class ChangeContextUtil {
}
}
if (scope instanceof PsiClass){
if (thisAccessExpr != null){
if (scope instanceof PsiClass) {
if (thisAccessExpr != null) {
thisAccessExpr = (PsiExpression)qualifyThis(thisAccessExpr, thisClass);
}
}
for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){
child = decodeContextInfo(child, thisClass, thisAccessExpr);
PsiElement child = scope.getFirstChild();
while (child != null) {
child = decodeContextInfo(child, thisClass, thisAccessExpr).getNextSibling();
}
return scope;
@@ -16,16 +16,21 @@
package com.intellij.refactoring.move.moveMembers;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.refactoring.util.EnumConstantsUtil;
import com.intellij.refactoring.util.RefactoringHierarchyUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -33,12 +38,13 @@ import java.util.*;
/**
* @author Maxim.Medvedev
*/
public class MoveJavaMemberHandler implements MoveMemberHandler {
public MoveMembersProcessor.MoveMembersUsageInfo getUsage(PsiMember member,
PsiReference psiReference,
Set<PsiMember> membersToMove,
PsiClass targetClass) {
@Override
@Nullable
public MoveMembersProcessor.MoveMembersUsageInfo getUsage(@NotNull PsiMember member,
@NotNull PsiReference psiReference,
@NotNull Set<PsiMember> membersToMove,
@NotNull PsiClass targetClass) {
PsiElement ref = psiReference.getElement();
if (ref instanceof PsiReferenceExpression) {
PsiReferenceExpression refExpr = (PsiReferenceExpression)ref;
@@ -70,15 +76,102 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
return null;
}
private static boolean isInMovedElement(PsiElement element, Set<PsiMember> membersToMove) {
protected static boolean isInMovedElement(PsiElement element, Set<PsiMember> membersToMove) {
for (PsiMember member : membersToMove) {
if (PsiTreeUtil.isAncestor(member, element, false)) return true;
}
return false;
}
public boolean changeExternalUsage(MoveMembersOptions options, MoveMembersProcessor.MoveMembersUsageInfo usage) {
if (!usage.getElement().isValid()) return true;
@Override
public void checkConflictsOnUsage(@NotNull MoveMembersProcessor.MoveMembersUsageInfo usageInfo,
@Nullable String newVisibility,
@Nullable PsiModifierList modifierListCopy,
@NotNull PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
final PsiElement element = usageInfo.getElement();
if (element == null) return;
final PsiMember member = usageInfo.member;
if (element instanceof PsiReferenceExpression) {
PsiExpression qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
PsiClass accessObjectClass = null;
if (qualifier != null) {
accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
}
if (!JavaResolveUtil.isAccessible(member, targetClass, modifierListCopy, element, accessObjectClass, null)) {
String visibility = newVisibility != null ? newVisibility : VisibilityUtil.getVisibilityStringToDisplay(member);
String message = RefactoringBundle.message("0.with.1.visibility.is.not.accessible.from.2",
RefactoringUIUtil.getDescription(member, false),
visibility,
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(element), true));
conflicts.putValue(member, CommonRefactoringUtil.capitalize(message));
}
}
if (member instanceof PsiField && targetClass.isInterface()) {
ReadWriteAccessDetector accessDetector = ReadWriteAccessDetector.findDetector(member);
if (accessDetector != null) {
ReadWriteAccessDetector.Access access = accessDetector.getExpressionAccess(element);
if (access != ReadWriteAccessDetector.Access.Read) {
String message = RefactoringUIUtil.getDescription(member, true) + " has write access but is moved to an interface";
conflicts.putValue(element, CommonRefactoringUtil.capitalize(message));
}
}
}
final PsiReference reference = usageInfo.getReference();
if (reference != null) {
RefactoringConflictsUtil.checkAccessibilityConflicts(reference, member, modifierListCopy, targetClass, membersToMove, conflicts);
}
}
@Override
public void checkConflictsOnMember(@NotNull PsiMember member,
@Nullable String newVisibility,
@Nullable PsiModifierList modifierListCopy,
@NotNull PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
if (member instanceof PsiMethod && hasMethod(targetClass, (PsiMethod)member) ||
member instanceof PsiField && hasField(targetClass, (PsiField)member)) {
String message = RefactoringBundle.message("0.already.exists.in.the.target.class", RefactoringUIUtil.getDescription(member, false));
conflicts.putValue(member, CommonRefactoringUtil.capitalize(message));
}
RefactoringConflictsUtil.checkUsedElements(member, member, membersToMove, null, targetClass, targetClass, conflicts);
}
protected static boolean hasMethod(PsiClass targetClass, PsiMethod method) {
PsiMethod[] targetClassMethods = targetClass.getMethods();
for (PsiMethod candidate : targetClassMethods) {
if (candidate != method &&
MethodSignatureUtil.areSignaturesEqual(method.getSignature(PsiSubstitutor.EMPTY),
candidate.getSignature(PsiSubstitutor.EMPTY))) {
return true;
}
}
return false;
}
protected static boolean hasField(PsiClass targetClass, PsiField field) {
String fieldName = field.getName();
PsiField[] targetClassFields = targetClass.getFields();
for (PsiField candidate : targetClassFields) {
if (candidate != field &&
fieldName.equals(candidate.getName())) {
return true;
}
}
return false;
}
@Override
public boolean changeExternalUsage(@NotNull MoveMembersOptions options, @NotNull MoveMembersProcessor.MoveMembersUsageInfo usage) {
final PsiElement element = usage.getElement();
if (element == null || !element.isValid()) return true;
if (usage.reference instanceof PsiReferenceExpression) {
PsiReferenceExpression refExpr = (PsiReferenceExpression)usage.reference;
@@ -101,14 +194,24 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
return false;
}
public PsiMember doMove(MoveMembersOptions options, PsiMember member, PsiElement anchor, PsiClass targetClass) {
protected static void changeQualifier(PsiReferenceExpression refExpr, PsiClass aClass, PsiMember member) throws IncorrectOperationException {
if (RefactoringUtil.hasOnDemandStaticImport(refExpr, aClass)) {
refExpr.setQualifierExpression(null);
}
else if (!RefactoringUtil.hasStaticImportOn(refExpr, member)){
PsiElementFactory factory = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory();
refExpr.setQualifierExpression(factory.createReferenceExpression(aClass));
}
}
@Override
@NotNull
public PsiMember doMove(@NotNull MoveMembersOptions options, @NotNull PsiMember member, PsiElement anchor, @NotNull PsiClass targetClass) {
if (member instanceof PsiVariable) {
((PsiVariable)member).normalizeDeclaration();
}
ChangeContextUtil.encodeContextInfo(member, true);
if (targetClass == null) return null;
final PsiMember memberCopy;
if (options.makeEnumConstant() &&
@@ -118,8 +221,9 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
}
else {
memberCopy = (PsiMember)member.copy();
if (member.getContainingClass().isInterface() && !targetClass.isInterface()) {
//might need to make modifiers explicit, see IDEADEV-11416
final PsiClass containingClass = member.getContainingClass();
if (containingClass != null && containingClass.isInterface() && !targetClass.isInterface()) {
// might need to make modifiers explicit, see IDEADEV-11416
final PsiModifierList list = memberCopy.getModifierList();
assert list != null;
list.setModifierProperty(PsiModifier.STATIC, member.hasModifierProperty(PsiModifier.STATIC));
@@ -131,22 +235,14 @@ public class MoveJavaMemberHandler implements MoveMemberHandler {
return anchor != null ? (PsiMember)targetClass.addAfter(memberCopy, anchor) : (PsiMember)targetClass.add(memberCopy);
}
public void decodeContextInfo(PsiElement scope) {
@Override
public void decodeContextInfo(@NotNull PsiElement scope) {
ChangeContextUtil.decodeContextInfo(scope, null, null);
}
private static void changeQualifier(PsiReferenceExpression refExpr, PsiClass aClass, PsiMember member) throws IncorrectOperationException {
if (RefactoringUtil.hasOnDemandStaticImport(refExpr, aClass)) {
refExpr.setQualifierExpression(null);
}
else if (!RefactoringUtil.hasStaticImportOn(refExpr, member)){
PsiElementFactory factory = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory();
refExpr.setQualifierExpression(factory.createReferenceExpression(aClass));
}
}
@Override
@Nullable
public PsiElement getAnchor(final PsiMember member, final PsiClass targetClass) {
public PsiElement getAnchor(@NotNull final PsiMember member, @NotNull final PsiClass targetClass) {
if (member instanceof PsiField && member.hasModifierProperty(PsiModifier.STATIC)) {
final List<PsiField> afterFields = new ArrayList<PsiField>();
final PsiExpression psiExpression = ((PsiField)member).getInitializer();
@@ -16,10 +16,9 @@
package com.intellij.refactoring.move.moveMembers;
import com.intellij.lang.LanguageExtension;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiReference;
import com.intellij.psi.*;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@@ -30,17 +29,36 @@ import java.util.Set;
public interface MoveMemberHandler {
LanguageExtension<MoveMemberHandler> EP_NAME = new LanguageExtension<MoveMemberHandler>("com.intellij.refactoring.moveMemberHandler");
MoveMembersProcessor.MoveMembersUsageInfo getUsage(PsiMember member,
PsiReference ref,
Set<PsiMember> membersToMove,
PsiClass targetClass);
@Nullable
MoveMembersProcessor.MoveMembersUsageInfo getUsage(@NotNull PsiMember member,
@NotNull PsiReference ref,
@NotNull Set<PsiMember> membersToMove,
@NotNull PsiClass targetClass);
boolean changeExternalUsage(MoveMembersOptions options, MoveMembersProcessor.MoveMembersUsageInfo usage);
void checkConflictsOnUsage(@NotNull MoveMembersProcessor.MoveMembersUsageInfo usageInfo,
@Nullable String newVisibility,
@Nullable PsiModifierList modifierListCopy,
@NotNull PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts);
PsiMember doMove(MoveMembersOptions options, PsiMember member, PsiElement anchor, PsiClass targetClass);
void decodeContextInfo(PsiElement scope);
void checkConflictsOnMember(@NotNull PsiMember member,
@Nullable String newVisibility,
@Nullable PsiModifierList modifierListCopy,
@NotNull PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts);
@Nullable
PsiElement getAnchor(PsiMember member, PsiClass targetClass);
PsiElement getAnchor(@NotNull PsiMember member, @NotNull PsiClass targetClass);
boolean changeExternalUsage(@NotNull MoveMembersOptions options, @NotNull MoveMembersProcessor.MoveMembersUsageInfo usage);
@NotNull
PsiMember doMove(@NotNull MoveMembersOptions options,
@NotNull PsiMember member,
@Nullable PsiElement anchor,
@NotNull PsiClass targetClass);
void decodeContextInfo(@NotNull PsiElement scope);
}
@@ -15,17 +15,13 @@
*/
package com.intellij.refactoring.move.moveMembers;
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.resolve.JavaResolveUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.HelpID;
@@ -34,7 +30,9 @@ import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.move.MoveCallback;
import com.intellij.refactoring.move.MoveHandler;
import com.intellij.refactoring.move.MoveMemberViewDescriptor;
import com.intellij.refactoring.util.*;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.MoveRenameUsageInfo;
import com.intellij.refactoring.util.RefactoringConflictsUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewUtil;
@@ -44,6 +42,7 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
@@ -61,16 +60,16 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
private String myCommandName = MoveMembersImpl.REFACTORING_NAME;
private MoveMembersOptions myOptions;
public MoveMembersProcessor(Project project, MoveCallback moveCallback, MoveMembersOptions options) {
public MoveMembersProcessor(Project project, MoveMembersOptions options) {
this(project, null, options);
}
public MoveMembersProcessor(Project project, @Nullable MoveCallback moveCallback, MoveMembersOptions options) {
super(project);
myMoveCallback = moveCallback;
setOptions(options);
}
public MoveMembersProcessor(Project project, MoveMembersOptions options) {
this(project, null, options);
}
protected String getCommandName() {
return myCommandName;
}
@@ -152,10 +151,11 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
protected void performRefactoring(final UsageInfo[] usages) {
try {
// correct references to moved members from the outside
PsiClass targetClass = JavaPsiFacade.getInstance(myProject).findClass(myOptions.getTargetClassName(),
GlobalSearchScope.projectScope(myProject));
if (targetClass == null) return;
// collect anchors to place moved members at
final Map<PsiMember, PsiElement> anchors = new HashMap<PsiMember, PsiElement>();
for (PsiMember member : myMembersToMove) {
final MoveMemberHandler handler = MoveMemberHandler.EP_NAME.forLanguage(member.getLanguage());
@@ -163,12 +163,14 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
anchors.put(member, handler.getAnchor(member, targetClass));
}
}
// correct references to moved members from the outside
ArrayList<MoveMembersUsageInfo> otherUsages = new ArrayList<MoveMembersUsageInfo>();
for (UsageInfo usageInfo : usages) {
MoveMembersUsageInfo usage = (MoveMembersUsageInfo)usageInfo;
if (!usage.reference.isValid()) continue;
final MoveMemberHandler handler = MoveMemberHandler.EP_NAME.forLanguage(usageInfo.getElement().getLanguage());
if (handler!=null) {
if (handler != null) {
if (handler.changeExternalUsage(myOptions, usage)) continue;
}
otherUsages.add(usage);
@@ -190,7 +192,7 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
final RefactoringElementListener elementListener = getTransaction().getElementListener(member);
final MoveMemberHandler handler = MoveMemberHandler.EP_NAME.forLanguage(member.getLanguage());
if (handler != null) {
PsiMember newMember=handler.doMove(myOptions, member, anchors.get(member), targetClass);
PsiMember newMember = handler.doMove(myOptions, member, anchors.get(member), targetClass);
elementListener.elementMoved(newMember);
fixModifierList(newMember, usages);
@@ -201,7 +203,6 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
}
// qualifier info must be decoded after members are moved
//ChangeContextUtil.decodeContextInfo(myTargetClass, null, null);
final MoveMemberHandler handler = MoveMemberHandler.EP_NAME.forLanguage(myTargetClass.getLanguage());
if (handler != null) handler.decodeContextInfo(myTargetClass);
@@ -236,65 +237,63 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
final UsageInfo[] usages = refUsages.get();
try {
addInaccessibleConflicts(conflicts, usages);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
analyzeMoveConflicts(myMembersToMove, myTargetClass, myNewVisibility, conflicts);
RefactoringConflictsUtil.analyzeModuleConflicts(myProject, myMembersToMove, usages, myTargetClass, conflicts);
return showConflicts(conflicts, usages);
}
private void addInaccessibleConflicts(final MultiMap<PsiElement, String> conflicts, final UsageInfo[] usages) throws IncorrectOperationException {
String newVisibility = myNewVisibility;
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(newVisibility)) { //Still need to check for access object
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(newVisibility)) { // still need to check for access object
newVisibility = PsiModifier.PUBLIC;
}
Map<PsiMember, PsiModifierList> modifierListCopies = new HashMap<PsiMember, PsiModifierList>();
final Map<PsiMember, PsiModifierList> modifierListCopies = new HashMap<PsiMember, PsiModifierList>();
for (PsiMember member : myMembersToMove) {
PsiModifierList copy = member.getModifierList();
if (copy != null) copy = (PsiModifierList)copy.copy();
if (newVisibility != null) {
if (copy != null) VisibilityUtil.setVisibility(copy, newVisibility);
PsiModifierList modifierListCopy = member.getModifierList();
if (modifierListCopy != null) {
modifierListCopy = (PsiModifierList)modifierListCopy.copy();
}
modifierListCopies.put(member, copy);
if (modifierListCopy != null && newVisibility != null) {
try {
VisibilityUtil.setVisibility(modifierListCopy, newVisibility);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
modifierListCopies.put(member, modifierListCopy);
}
analyzeConflictsOnUsages(usages, myMembersToMove, newVisibility, myTargetClass, modifierListCopies, conflicts);
analyzeConflictsOnMembers(myMembersToMove, newVisibility, myTargetClass, modifierListCopies, conflicts);
RefactoringConflictsUtil.analyzeModuleConflicts(myProject, myMembersToMove, usages, myTargetClass, conflicts);
return showConflicts(conflicts, usages);
}
private static void analyzeConflictsOnUsages(UsageInfo[] usages,
Set<PsiMember> membersToMove,
String newVisibility,
PsiClass targetClass,
Map<PsiMember, PsiModifierList> modifierListCopies,
MultiMap<PsiElement, String> conflicts) {
for (UsageInfo usage : usages) {
if (usage instanceof MoveMembersUsageInfo) {
final MoveMembersUsageInfo usageInfo = (MoveMembersUsageInfo)usage;
PsiElement element = usage.getElement();
if (element != null) {
final PsiMember member = usageInfo.member;
if (element instanceof PsiReferenceExpression) {
PsiExpression qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
PsiClass accessObjectClass = null;
if (qualifier != null) {
accessObjectClass = (PsiClass)PsiUtil.getAccessObjectClass(qualifier).getElement();
}
if (!(usage instanceof MoveMembersUsageInfo)) continue;
final MoveMembersUsageInfo usageInfo = (MoveMembersUsageInfo)usage;
final PsiMember member = usageInfo.member;
final MoveMemberHandler handler = MoveMemberHandler.EP_NAME.forLanguage(member.getLanguage());
if (handler != null) {
handler.checkConflictsOnUsage(usageInfo, newVisibility, modifierListCopies.get(member), targetClass, membersToMove, conflicts);
}
}
}
if (!JavaResolveUtil.isAccessible(member, myTargetClass, modifierListCopies.get(member), element, accessObjectClass, null)) {
newVisibility = newVisibility == null ? VisibilityUtil.getVisibilityStringToDisplay(member) : newVisibility;
String message =
CommonRefactoringUtil.capitalize(RefactoringBundle.message("0.with.1.visibility.is.not.accessible.from.2", RefactoringUIUtil.getDescription(member, false),
newVisibility, RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(element), true)));
conflicts.putValue(member, message);
}
}
if (member instanceof PsiField && myTargetClass.isInterface()) {
final ReadWriteAccessDetector accessDetector = ReadWriteAccessDetector.findDetector(member);
if (accessDetector != null) {
final ReadWriteAccessDetector.Access access = accessDetector.getExpressionAccess(element);
if (access != ReadWriteAccessDetector.Access.Read) {
conflicts.putValue(element, CommonRefactoringUtil.capitalize(RefactoringUIUtil.getDescription(member, true)) + " has write access but is moved to an interface");
}
}
}
}
private static void analyzeConflictsOnMembers(Set<PsiMember> membersToMove,
String newVisibility,
PsiClass targetClass,
Map<PsiMember, PsiModifierList> modifierListCopies,
MultiMap<PsiElement, String> conflicts) {
for (final PsiMember member : membersToMove) {
final MoveMemberHandler handler = MoveMemberHandler.EP_NAME.forLanguage(member.getLanguage());
if (handler != null) {
handler.checkConflictsOnMember(member, newVisibility, modifierListCopies.get(member), targetClass, membersToMove, conflicts);
}
}
}
@@ -308,53 +307,6 @@ public class MoveMembersProcessor extends BaseRefactoringProcessor {
super.doRun();
}
private static void analyzeMoveConflicts(@NotNull Set<PsiMember> membersToMove, final PsiClass targetClass, final String newVisibility,
MultiMap<PsiElement, String> conflicts) {
for (final PsiMember member : membersToMove) {
if (member instanceof PsiMethod) {
PsiMethod method = (PsiMethod)member;
if (hasMethod(targetClass, method)) {
String message = RefactoringBundle.message("0.already.exists.in.the.target.class", RefactoringUIUtil.getDescription(method, false));
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(method, message);
}
}
else if (member instanceof PsiField) {
PsiField field = (PsiField)member;
if (hasField(targetClass, field)) {
String message = RefactoringBundle.message("0.already.exists.in.the.target.class", RefactoringUIUtil.getDescription(field, false));
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(field, message);
}
}
}
RefactoringConflictsUtil.analyzeAccessibilityConflicts(membersToMove, targetClass, conflicts, newVisibility);
}
private static boolean hasMethod(PsiClass targetClass, PsiMethod method) {
PsiMethod[] targetClassMethods = targetClass.getMethods();
for (PsiMethod candidate : targetClassMethods) {
if (candidate != method &&
MethodSignatureUtil.areSignaturesEqual(method.getSignature(PsiSubstitutor.EMPTY),
candidate.getSignature(PsiSubstitutor.EMPTY))) {
return true;
}
}
return false;
}
private static boolean hasField(PsiClass targetClass, PsiField field) {
String fieldName = field.getName();
PsiField[] targetClassFields = targetClass.getFields();
for (PsiField candidate : targetClassFields) {
if (candidate != field &&
fieldName.equals(candidate.getName())) {
return true;
}
}
return false;
}
public List<PsiElement> getMembers() {
return new ArrayList<PsiElement>(myMembersToMove);
}
@@ -48,13 +48,16 @@ public class RefactoringConflictsUtil {
private RefactoringConflictsUtil() { }
public static void analyzeAccessibilityConflicts(@NotNull Set<PsiMember> membersToMove,
@NotNull final PsiClass targetClass,
final MultiMap<PsiElement, String> conflicts, String newVisibility) {
@NotNull PsiClass targetClass,
@NotNull MultiMap<PsiElement, String> conflicts,
@Nullable String newVisibility) {
analyzeAccessibilityConflicts(membersToMove, targetClass, conflicts, newVisibility, targetClass, null);
}
public static void analyzeAccessibilityConflicts(@NotNull Set<PsiMember> membersToMove, @Nullable final PsiClass targetClass, final MultiMap<PsiElement, String> conflicts,
String newVisibility,
public static void analyzeAccessibilityConflicts(@NotNull Set<PsiMember> membersToMove,
@Nullable PsiClass targetClass,
@NotNull MultiMap<PsiElement, String> conflicts,
@Nullable String newVisibility,
@NotNull PsiElement context,
@Nullable Set<PsiMethod> abstractMethods) {
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(newVisibility)) { //Still need to check for access object
@@ -63,41 +66,65 @@ public class RefactoringConflictsUtil {
for (PsiMember member : membersToMove) {
checkUsedElements(member, member, membersToMove, abstractMethods, targetClass, context, conflicts);
checkAccessibilityConflicts(member, newVisibility, targetClass, membersToMove, conflicts);
}
}
PsiModifierList modifierList = member.getModifierList();
if (modifierList != null) modifierList = (PsiModifierList)modifierList.copy();
if (newVisibility != null) {
try {
if (modifierList != null) VisibilityUtil.setVisibility(modifierList, newVisibility);
}
catch (IncorrectOperationException ex) {
/* do nothing and hope for the best */
}
public static void checkAccessibilityConflicts(@NotNull PsiMember member,
@Nullable String newVisibility,
@Nullable PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
PsiModifierList modifierListCopy = member.getModifierList();
if (modifierListCopy != null) {
modifierListCopy = (PsiModifierList)modifierListCopy.copy();
}
if (newVisibility != null && modifierListCopy != null) {
try {
VisibilityUtil.setVisibility(modifierListCopy, newVisibility);
}
JavaPsiFacade manager = JavaPsiFacade.getInstance(member.getProject());
for (PsiReference psiReference : ReferencesSearch.search(member)) {
PsiElement ref = psiReference.getElement();
if (!RefactoringHierarchyUtil.willBeInTargetClass(ref, membersToMove, targetClass, false)) {
//Check for target class accessibility
if (targetClass != null && !manager.getResolveHelper().isAccessible(targetClass, targetClass.getModifierList(), ref, null, null)) {
String message = RefactoringBundle.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
RefactoringUIUtil.getDescription(targetClass, true),
VisibilityUtil.getVisibilityStringToDisplay(targetClass),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(ref), true));
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(targetClass, message);
}
//check for member accessibility
else if (!manager.getResolveHelper().isAccessible(member, modifierList, ref, null, null)) {
String message = RefactoringBundle.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
RefactoringUIUtil.getDescription(member, true),
VisibilityUtil.getVisibilityStringToDisplay(member),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(ref), true));
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(member, message);
}
}
catch (IncorrectOperationException ignore) { } // do nothing and hope for the best
}
checkAccessibilityConflicts(member, modifierListCopy, targetClass, membersToMove, conflicts);
}
public static void checkAccessibilityConflicts(@NotNull PsiMember member,
@Nullable PsiModifierList modifierListCopy,
@Nullable PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
for (PsiReference psiReference : ReferencesSearch.search(member)) {
checkAccessibilityConflicts(psiReference, member, modifierListCopy, targetClass, membersToMove, conflicts);
}
}
public static void checkAccessibilityConflicts(@NotNull PsiReference reference,
@NotNull PsiMember member,
@Nullable PsiModifierList modifierListCopy,
@Nullable PsiClass targetClass,
@NotNull Set<PsiMember> membersToMove,
@NotNull MultiMap<PsiElement, String> conflicts) {
JavaPsiFacade manager = JavaPsiFacade.getInstance(member.getProject());
PsiElement ref = reference.getElement();
if (!RefactoringHierarchyUtil.willBeInTargetClass(ref, membersToMove, targetClass, false)) {
// check for target class accessibility
if (targetClass != null && !manager.getResolveHelper().isAccessible(targetClass, targetClass.getModifierList(), ref, null, null)) {
String message = RefactoringBundle.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
RefactoringUIUtil.getDescription(targetClass, true),
VisibilityUtil.getVisibilityStringToDisplay(targetClass),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(ref), true));
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(targetClass, message);
}
// check for member accessibility
else if (!manager.getResolveHelper().isAccessible(member, modifierListCopy, ref, null, null)) {
String message = RefactoringBundle.message("0.is.1.and.will.not.be.accessible.from.2.in.the.target.class",
RefactoringUIUtil.getDescription(member, true),
VisibilityUtil.getVisibilityStringToDisplay(member),
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(ref), true));
message = CommonRefactoringUtil.capitalize(message);
conflicts.putValue(member, message);
}
}
}
@@ -155,7 +182,7 @@ public class RefactoringConflictsUtil {
public static void checkAccessibility(PsiMember refMember,
@NotNull PsiElement newContext,
PsiClass accessClass,
@Nullable PsiClass accessClass,
PsiMember member,
MultiMap<PsiElement, String> conflicts) {
if (!PsiUtil.isAccessible(refMember, newContext, accessClass)) {
@@ -176,24 +203,24 @@ public class RefactoringConflictsUtil {
}
}
public static void analyzeModuleConflicts(Project project,
Collection<? extends PsiElement> scope,
public static void analyzeModuleConflicts(final Project project,
final Collection<? extends PsiElement> scopes,
final UsageInfo[] usages,
PsiElement target,
final PsiElement target,
final MultiMap<PsiElement,String> conflicts) {
if (scope == null) return;
if (scopes == null) return;
final VirtualFile vFile = PsiUtilBase.getVirtualFile(target);
if (vFile == null) return;
analyzeModuleConflicts(project, scope, usages, vFile, conflicts);
analyzeModuleConflicts(project, scopes, usages, vFile, conflicts);
}
public static void analyzeModuleConflicts(Project project,
public static void analyzeModuleConflicts(final Project project,
final Collection<? extends PsiElement> scopes,
final UsageInfo[] usages,
final VirtualFile vFile,
final MultiMap<PsiElement, String> conflicts) {
if (scopes == null) return;
for (final PsiElement scope : scopes) {
if (scope instanceof PsiPackage || scope instanceof PsiDirectory) return;
}
@@ -207,16 +234,16 @@ public class RefactoringConflictsUtil {
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
final PsiElement resolved = reference.resolve();
if (resolved != null && !reported.contains(resolved) && !CommonRefactoringUtil.isAncestor(resolved, scopes) &&
if (resolved != null &&
!reported.contains(resolved) &&
!CommonRefactoringUtil.isAncestor(resolved, scopes) &&
!PsiSearchScopeUtil.isInScope(resolveScope, resolved)) {
final String scopeDescription =
RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(reference), true);
final String scopeDescription = RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(reference), true);
final String message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.in.module.2",
CommonRefactoringUtil.capitalize(
RefactoringUIUtil.getDescription(resolved, true)), scopeDescription,
CommonRefactoringUtil.htmlEmphasize(
targetModule.getName()));
conflicts.putValue(resolved, message);
RefactoringUIUtil.getDescription(resolved, true),
scopeDescription,
CommonRefactoringUtil.htmlEmphasize(targetModule.getName()));
conflicts.putValue(resolved, CommonRefactoringUtil.capitalize(message));
reported.add(resolved);
}
}
@@ -255,19 +282,17 @@ public class RefactoringConflictsUtil {
assert referencedElement != null : moveRenameUsageInfo;
if (module == targetModule && isInTestSources) {
message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.from.production.of.module.2",
CommonRefactoringUtil.capitalize(
RefactoringUIUtil.getDescription(referencedElement, true)),
RefactoringUIUtil.getDescription(referencedElement, true),
scopeDescription,
CommonRefactoringUtil.htmlEmphasize(module.getName()));
}
else {
message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.from.module.2",
CommonRefactoringUtil.capitalize(
RefactoringUIUtil.getDescription(referencedElement, true)),
RefactoringUIUtil.getDescription(referencedElement, true),
scopeDescription,
CommonRefactoringUtil.htmlEmphasize(module.getName()));
}
conflicts.putValue(referencedElement, message);
conflicts.putValue(referencedElement, CommonRefactoringUtil.capitalize(message));
}
}
}
@@ -275,4 +300,4 @@ public class RefactoringConflictsUtil {
}
}
}
}
}