mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
push down delegate to allow inter language pushes (inspired by java -> kotlin)
This commit is contained in:
+4
-8
@@ -213,7 +213,7 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
@Override
|
||||
protected boolean preprocessUsages(@NotNull final Ref<UsageInfo[]> refUsages) {
|
||||
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
|
||||
final PushDownConflicts pushDownConflicts = new PushDownConflicts(mySuperClass, myMemberInfos);
|
||||
final PushDownConflicts pushDownConflicts = new PushDownConflicts(mySuperClass, myMemberInfos, conflicts);
|
||||
for (PsiClass targetClass : myTargetClasses) {
|
||||
for (MemberInfo info : myMemberInfos) {
|
||||
final PsiMember member = info.getMember();
|
||||
@@ -221,10 +221,6 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
}
|
||||
//todo check accessibility conflicts
|
||||
}
|
||||
final MultiMap<PsiElement, String> conflictsMap = pushDownConflicts.getConflicts();
|
||||
for (PsiElement element : conflictsMap.keySet()) {
|
||||
conflicts.put(element, conflictsMap.get(element));
|
||||
}
|
||||
if (myCurrentInheritor != null) {
|
||||
ReferencesSearch.search(myCurrentInheritor).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
@@ -276,7 +272,8 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
}
|
||||
|
||||
protected void performRefactoring(@NotNull final UsageInfo[] usages) {
|
||||
new PushDownProcessor(mySuperClass.getProject(), myMemberInfos, mySuperClass, new DocCommentPolicy(myPolicy)) {
|
||||
final DocCommentPolicy docPolicy = new DocCommentPolicy(myPolicy);
|
||||
new PushDownProcessor(mySuperClass, myMemberInfos, docPolicy) {
|
||||
//push down conflicts are already collected
|
||||
@Override
|
||||
protected boolean showConflicts(@NotNull MultiMap<PsiElement, String> conflicts, UsageInfo[] usages) {
|
||||
@@ -286,8 +283,7 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
@Override
|
||||
protected void performRefactoring(@NotNull UsageInfo[] pushDownUsages) {
|
||||
if (myCurrentInheritor != null) {
|
||||
encodeRefs();
|
||||
pushDownToClass(myCurrentInheritor);
|
||||
pushDownToDedicatedClass(myCurrentInheritor);
|
||||
} else {
|
||||
super.performRefactoring(pushDownUsages);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil;
|
||||
import com.intellij.codeInsight.intention.impl.CreateClassDialog;
|
||||
import com.intellij.codeInsight.intention.impl.CreateSubclassAction;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
|
||||
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.TypeConversionUtil;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
||||
import com.intellij.refactoring.listeners.JavaRefactoringListenerManager;
|
||||
import com.intellij.refactoring.listeners.impl.JavaRefactoringListenerManagerImpl;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class JavaPushDownDelegate extends PushDownDelegate {
|
||||
public static final Key<Boolean> REMOVE_QUALIFIER_KEY = Key.create("REMOVE_QUALIFIER_KEY");
|
||||
public static final Key<PsiClass> REPLACE_QUALIFIER_KEY = Key.create("REPLACE_QUALIFIER_KEY");
|
||||
|
||||
private static final Logger LOG = Logger.getInstance("#" + JavaPushDownDelegate.class.getName());
|
||||
|
||||
@Override
|
||||
public boolean isApplicableForSource(@NotNull PsiElement sourceClass) {
|
||||
return sourceClass.getLanguage().isKindOf(JavaLanguage.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PsiElement> findInheritors(PushDownData pushDownData) {
|
||||
final List<PsiElement> result = new ArrayList<>();
|
||||
final PsiClass aClass = (PsiClass)pushDownData.getSourceClass();
|
||||
ClassInheritorsSearch.search(aClass, false).forEach((iClass) -> {
|
||||
result.add(iClass);
|
||||
return true;
|
||||
});
|
||||
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(aClass);
|
||||
if (interfaceMethod != null) {
|
||||
for (MemberInfoBase<? extends PsiElement> info : pushDownData.getMembersToMove()) {
|
||||
if (interfaceMethod == info.getMember()) {
|
||||
FunctionalExpressionSearch.search(aClass).forEach(expression -> {
|
||||
result.add(expression);
|
||||
return true;
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSourceClassConflicts(PushDownData pushDownData, MultiMap<PsiElement, String> conflicts) {
|
||||
new PushDownConflicts((PsiClass)pushDownData.getSourceClass(), (MemberInfo[])pushDownData.getMembersToMove(), conflicts).checkSourceClassConflicts();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkTargetClassConflicts(PsiElement targetClass,
|
||||
PushDownData pushDownData,
|
||||
boolean checkStatic,
|
||||
PsiElement context,
|
||||
MultiMap<PsiElement, String> conflicts) {
|
||||
new PushDownConflicts((PsiClass)pushDownData.getSourceClass(), (MemberInfo[])pushDownData.getMembersToMove(), conflicts).checkTargetClassConflicts(targetClass, checkStatic, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewSubClassData preprocessNoInheritorsFound(PsiElement sourceClass, String conflictDialogTitle) {
|
||||
final PsiClass aClass = (PsiClass)sourceClass;
|
||||
if (aClass.isEnum() || aClass.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
if (Messages.showOkCancelDialog((aClass.isEnum() ? "Enum " + aClass.getQualifiedName() + " doesn't have constants to inline to. " : "Final class " + aClass.getQualifiedName() + "does not have inheritors. ") +
|
||||
"Pushing members down will result in them being deleted. " +
|
||||
"Would you like to proceed?", conflictDialogTitle, Messages.getWarningIcon()) != Messages.OK) {
|
||||
return NewSubClassData.EMPTY;
|
||||
}
|
||||
} else {
|
||||
String noInheritors = aClass.isInterface() ?
|
||||
RefactoringBundle.message("interface.0.does.not.have.inheritors", aClass.getQualifiedName()) :
|
||||
RefactoringBundle.message("class.0.does.not.have.inheritors", aClass.getQualifiedName());
|
||||
final String message = noInheritors + "\n" + RefactoringBundle.message("push.down.will.delete.members");
|
||||
final int answer = Messages.showYesNoCancelDialog(message, conflictDialogTitle, Messages.getWarningIcon());
|
||||
if (answer == Messages.YES) {
|
||||
final CreateClassDialog classDialog = CreateSubclassAction.chooseSubclassToCreate(aClass);
|
||||
if (classDialog != null) {
|
||||
return new NewSubClassData(classDialog.getTargetDirectory(), classDialog.getClassName());
|
||||
} else {
|
||||
return NewSubClassData.EMPTY;
|
||||
}
|
||||
}
|
||||
else if (answer != Messages.NO) {
|
||||
return NewSubClassData.EMPTY;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareToPush(PushDownData pushDownData) {
|
||||
final Set<PsiMember> movedMembers = new HashSet<PsiMember>();
|
||||
for (MemberInfoBase<? extends PsiElement> memberInfo : pushDownData.getMembersToMove()) {
|
||||
movedMembers.add((PsiMember)memberInfo.getMember());
|
||||
}
|
||||
|
||||
for (MemberInfoBase<? extends PsiElement> memberInfo : pushDownData.getMembersToMove()) {
|
||||
final PsiElement member = memberInfo.getMember();
|
||||
member.accept(new JavaRecursiveElementVisitor() {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
encodeRef((PsiClass)pushDownData.getSourceClass(), expression, movedMembers, expression);
|
||||
super.visitReferenceExpression(expression);
|
||||
}
|
||||
|
||||
@Override public void visitNewExpression(PsiNewExpression expression) {
|
||||
final PsiJavaCodeReferenceElement classReference = expression.getClassReference();
|
||||
if (classReference != null) {
|
||||
encodeRef((PsiClass)pushDownData.getSourceClass(), classReference, movedMembers, expression);
|
||||
}
|
||||
super.visitNewExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeElement(final PsiTypeElement type) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = type.getInnermostComponentReferenceElement();
|
||||
if (referenceElement != null) {
|
||||
encodeRef((PsiClass)pushDownData.getSourceClass(), referenceElement, movedMembers, type);
|
||||
}
|
||||
super.visitTypeElement(type);
|
||||
}
|
||||
});
|
||||
ChangeContextUtil.encodeContextInfo(member, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pushDownToClass(PsiElement targetElement, PushDownData pushDownData) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(pushDownData.getSourceClass().getProject()).getElementFactory();
|
||||
final PsiClass targetClass = targetElement instanceof PsiClass ? (PsiClass)targetElement : null;
|
||||
if (targetClass == null) {
|
||||
return;
|
||||
}
|
||||
final PsiClass sourceClass = (PsiClass)pushDownData.getSourceClass();
|
||||
final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(sourceClass, targetClass, PsiSubstitutor.EMPTY);
|
||||
for (MemberInfoBase<? extends PsiElement> memberInfo : pushDownData.getMembersToMove()) {
|
||||
PsiMember member = (PsiMember)memberInfo.getMember();
|
||||
final List<PsiReference> refsToRebind = new ArrayList<PsiReference>();
|
||||
final PsiModifierList list = member.getModifierList();
|
||||
LOG.assertTrue(list != null);
|
||||
if (list.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
for (final PsiReference reference : ReferencesSearch.search(member)) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
final PsiExpression qualifierExpression = ((PsiReferenceExpression)element).getQualifierExpression();
|
||||
if (qualifierExpression instanceof PsiReferenceExpression && !(((PsiReferenceExpression)qualifierExpression).resolve() instanceof PsiClass)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
refsToRebind.add(reference);
|
||||
}
|
||||
}
|
||||
member = (PsiMember)member.copy();
|
||||
RefactoringUtil.replaceMovedMemberTypeParameters(member, PsiUtil.typeParametersIterable(sourceClass), substitutor, factory);
|
||||
PsiMember newMember = null;
|
||||
if (member instanceof PsiField) {
|
||||
((PsiField)member).normalizeDeclaration();
|
||||
if (sourceClass.isInterface() && !targetClass.isInterface()) {
|
||||
PsiUtil.setModifierProperty(member, PsiModifier.PUBLIC, true);
|
||||
PsiUtil.setModifierProperty(member, PsiModifier.STATIC, true);
|
||||
PsiUtil.setModifierProperty(member, PsiModifier.FINAL, true);
|
||||
}
|
||||
newMember = (PsiMember)targetClass.add(member);
|
||||
}
|
||||
else if (member instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod)member;
|
||||
PsiMethod methodBySignature = MethodSignatureUtil.findMethodBySuperSignature(targetClass, method.getSignature(substitutor), false);
|
||||
if (methodBySignature == null) {
|
||||
newMember = (PsiMethod)targetClass.add(method);
|
||||
if (sourceClass.isInterface()) {
|
||||
if (!targetClass.isInterface()) {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.PUBLIC, true);
|
||||
if (newMember.hasModifierProperty(PsiModifier.DEFAULT)) {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.DEFAULT, false);
|
||||
}
|
||||
else {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.ABSTRACT, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (memberInfo.isToAbstract()) {
|
||||
if (newMember.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.PROTECTED, true);
|
||||
}
|
||||
pushDownData.getCommentPolicy().processNewJavaDoc(((PsiMethod)newMember).getDocComment());
|
||||
}
|
||||
if (memberInfo.isToAbstract()) {
|
||||
OverrideImplementUtil.annotateOnOverrideImplement((PsiMethod)newMember, targetClass, (PsiMethod)memberInfo.getMember());
|
||||
}
|
||||
}
|
||||
else { //abstract method: remove @Override
|
||||
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(methodBySignature, "java.lang.Override");
|
||||
if (annotation != null && !leaveOverrideAnnotation(sourceClass, substitutor, method)) {
|
||||
annotation.delete();
|
||||
}
|
||||
final PsiDocComment oldDocComment = method.getDocComment();
|
||||
if (oldDocComment != null) {
|
||||
final PsiDocComment docComment = methodBySignature.getDocComment();
|
||||
final int policy = pushDownData.getCommentPolicy().getJavaDocPolicy();
|
||||
if (policy == DocCommentPolicy.COPY || policy == DocCommentPolicy.MOVE) {
|
||||
if (docComment != null) {
|
||||
docComment.replace(oldDocComment);
|
||||
}
|
||||
else {
|
||||
methodBySignature.getParent().addBefore(oldDocComment, methodBySignature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member instanceof PsiClass) {
|
||||
if (Boolean.FALSE.equals(memberInfo.getOverrides())) {
|
||||
final PsiClass psiClass = (PsiClass)memberInfo.getMember();
|
||||
PsiClassType classType = null;
|
||||
if (!targetClass.isInheritor(psiClass, false)) {
|
||||
final PsiClassType[] types = ((MemberInfo)memberInfo).getSourceReferenceList().getReferencedTypes();
|
||||
for (PsiClassType type : types) {
|
||||
if (type.resolve() == psiClass) {
|
||||
classType = (PsiClassType)substitutor.substitute(type);
|
||||
}
|
||||
}
|
||||
PsiJavaCodeReferenceElement classRef = classType != null ? factory.createReferenceElementByType(classType) : factory.createClassReferenceElement(psiClass);
|
||||
if (psiClass.isInterface()) {
|
||||
targetClass.getImplementsList().add(classRef);
|
||||
} else {
|
||||
targetClass.getExtendsList().add(classRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
newMember = (PsiMember)targetClass.add(member);
|
||||
}
|
||||
}
|
||||
|
||||
if (newMember != null) {
|
||||
decodeRefs(sourceClass, newMember, targetClass);
|
||||
//rebind imports first
|
||||
Collections.sort(refsToRebind, Comparator.comparing(PsiReference::getElement, PsiUtil.BY_POSITION));
|
||||
for (PsiReference psiReference : refsToRebind) {
|
||||
JavaCodeStyleManager.getInstance(sourceClass.getProject()).shortenClassReferences(psiReference.bindToElement(newMember));
|
||||
}
|
||||
final JavaRefactoringListenerManager listenerManager = JavaRefactoringListenerManager.getInstance(newMember.getProject());
|
||||
((JavaRefactoringListenerManagerImpl)listenerManager).fireMemberMoved(sourceClass, newMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFromSourceClass(PushDownData pushDownData) {
|
||||
for (MemberInfoBase<? extends PsiElement> memberInfo : pushDownData.getMembersToMove()) {
|
||||
final PsiElement member = memberInfo.getMember();
|
||||
|
||||
if (member instanceof PsiField) {
|
||||
member.delete();
|
||||
}
|
||||
else if (member instanceof PsiMethod) {
|
||||
if (memberInfo.isToAbstract()) {
|
||||
final PsiMethod method = (PsiMethod)member;
|
||||
if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
PsiUtil.setModifierProperty(method, PsiModifier.PROTECTED, true);
|
||||
}
|
||||
RefactoringUtil.makeMethodAbstract((PsiClass)pushDownData.getSourceClass(), method);
|
||||
pushDownData.getCommentPolicy().processOldJavaDoc(method.getDocComment());
|
||||
}
|
||||
else {
|
||||
member.delete();
|
||||
}
|
||||
}
|
||||
else if (member instanceof PsiClass) {
|
||||
if (Boolean.FALSE.equals(memberInfo.getOverrides())) {
|
||||
RefactoringUtil.removeFromReferenceList(((PsiClass)pushDownData.getSourceClass()).getImplementsList(), (PsiClass)member);
|
||||
}
|
||||
else {
|
||||
member.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement createSubClass(PsiElement aClass, NewSubClassData subClassData) {
|
||||
return CreateSubclassAction.createSubclass((PsiClass)aClass, (PsiDirectory)subClassData.getContext(), subClassData.getNewClassName());
|
||||
}
|
||||
|
||||
private static boolean leaveOverrideAnnotation(PsiClass aClass, PsiSubstitutor substitutor, PsiMethod method) {
|
||||
final PsiMethod methodBySignature = MethodSignatureUtil.findMethodBySignature(aClass, method.getSignature(substitutor), false);
|
||||
if (methodBySignature == null) return false;
|
||||
final PsiMethod[] superMethods = methodBySignature.findDeepestSuperMethods();
|
||||
if (superMethods.length == 0) return false;
|
||||
final boolean is15 = !PsiUtil.isLanguageLevel6OrHigher(methodBySignature);
|
||||
if (is15) {
|
||||
for (PsiMethod psiMethod : superMethods) {
|
||||
final PsiClass psiClass = psiMethod.getContainingClass();
|
||||
if (psiClass != null && psiClass.isInterface()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void encodeRef(PsiClass aClass,
|
||||
final PsiJavaCodeReferenceElement expression,
|
||||
final Set<PsiMember> movedMembers,
|
||||
final PsiElement toPut) {
|
||||
final PsiElement resolved = expression.resolve();
|
||||
if (resolved == null) return;
|
||||
final PsiElement qualifier = expression.getQualifier();
|
||||
for (PsiMember movedMember : movedMembers) {
|
||||
if (movedMember.equals(resolved)) {
|
||||
if (qualifier == null) {
|
||||
toPut.putCopyableUserData(REMOVE_QUALIFIER_KEY, Boolean.TRUE);
|
||||
} else {
|
||||
if (qualifier instanceof PsiJavaCodeReferenceElement &&
|
||||
((PsiJavaCodeReferenceElement)qualifier).isReferenceTo(aClass)) {
|
||||
toPut.putCopyableUserData(REPLACE_QUALIFIER_KEY, aClass);
|
||||
}
|
||||
}
|
||||
} else if (movedMember instanceof PsiClass && PsiTreeUtil.getParentOfType(resolved, PsiClass.class, false) == movedMember) {
|
||||
if (qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).isReferenceTo(movedMember)) {
|
||||
toPut.putCopyableUserData(REPLACE_QUALIFIER_KEY, (PsiClass)movedMember);
|
||||
}
|
||||
} else {
|
||||
if (qualifier instanceof PsiThisExpression) {
|
||||
final PsiJavaCodeReferenceElement qElement = ((PsiThisExpression)qualifier).getQualifier();
|
||||
if (qElement != null && qElement.isReferenceTo(aClass)) {
|
||||
toPut.putCopyableUserData(REPLACE_QUALIFIER_KEY, aClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void decodeRefs(PsiClass sourceClass, final PsiMember member, final PsiClass targetClass) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(sourceClass.getProject()).getElementFactory();
|
||||
member.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
decodeRef(sourceClass, expression, factory, targetClass, expression);
|
||||
super.visitReferenceExpression(expression);
|
||||
}
|
||||
|
||||
@Override public void visitNewExpression(PsiNewExpression expression) {
|
||||
final PsiJavaCodeReferenceElement classReference = expression.getClassReference();
|
||||
if (classReference != null) decodeRef(sourceClass, classReference, factory, targetClass, expression);
|
||||
super.visitNewExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeElement(final PsiTypeElement type) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = type.getInnermostComponentReferenceElement();
|
||||
if (referenceElement != null) decodeRef(sourceClass, referenceElement, factory, targetClass, type);
|
||||
super.visitTypeElement(type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void decodeRef(PsiClass sourceClass,
|
||||
final PsiJavaCodeReferenceElement ref,
|
||||
final PsiElementFactory factory,
|
||||
final PsiClass targetClass,
|
||||
final PsiElement toGet) {
|
||||
try {
|
||||
if (toGet.getCopyableUserData(REMOVE_QUALIFIER_KEY) != null) {
|
||||
toGet.putCopyableUserData(REMOVE_QUALIFIER_KEY, null);
|
||||
final PsiElement qualifier = ref.getQualifier();
|
||||
if (qualifier != null) qualifier.delete();
|
||||
}
|
||||
else {
|
||||
PsiClass psiClass = toGet.getCopyableUserData(REPLACE_QUALIFIER_KEY);
|
||||
if (psiClass != null) {
|
||||
toGet.putCopyableUserData(REPLACE_QUALIFIER_KEY, null);
|
||||
PsiElement qualifier = ref.getQualifier();
|
||||
if (qualifier != null) {
|
||||
|
||||
if (psiClass == sourceClass) {
|
||||
psiClass = targetClass;
|
||||
} else if (psiClass.getContainingClass() == sourceClass) {
|
||||
psiClass = targetClass.findInnerClassByName(psiClass.getName(), false);
|
||||
LOG.assertTrue(psiClass != null);
|
||||
}
|
||||
|
||||
if (!(qualifier instanceof PsiThisExpression) && ref instanceof PsiReferenceExpression) {
|
||||
((PsiReferenceExpression)ref).setQualifierExpression(factory.createReferenceExpression(psiClass));
|
||||
}
|
||||
else {
|
||||
if (qualifier instanceof PsiThisExpression) {
|
||||
qualifier = ((PsiThisExpression)qualifier).getQualifier();
|
||||
}
|
||||
qualifier.replace(factory.createReferenceElementByType(factory.createType(psiClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public class PushDownConflicts {
|
||||
private final MultiMap<PsiElement, String> myConflicts;
|
||||
|
||||
|
||||
public PushDownConflicts(PsiClass aClass, MemberInfo[] memberInfos) {
|
||||
public PushDownConflicts(PsiClass aClass, MemberInfo[] memberInfos, MultiMap<PsiElement, String> conflicts) {
|
||||
myClass = aClass;
|
||||
|
||||
myMovedMembers = new HashSet<PsiMember>();
|
||||
@@ -52,7 +52,7 @@ public class PushDownConflicts {
|
||||
}
|
||||
}
|
||||
|
||||
myConflicts = new MultiMap<PsiElement, String>();
|
||||
myConflicts = conflicts;
|
||||
}
|
||||
|
||||
public boolean isAnyConflicts() {
|
||||
|
||||
@@ -117,9 +117,7 @@ public class PushDownDialog extends RefactoringDialog {
|
||||
|
||||
JavaRefactoringSettings.getInstance().PUSH_DOWN_PREVIEW_USAGES = isPreviewUsages();
|
||||
|
||||
invokeRefactoring (new PushDownProcessor(
|
||||
getProject(), getSelectedMemberInfos(), myClass,
|
||||
new DocCommentPolicy(getJavaDocPolicy())));
|
||||
invokeRefactoring(new PushDownProcessor(myClass, getSelectedMemberInfos(), new DocCommentPolicy(getJavaDocPolicy())));
|
||||
}
|
||||
|
||||
private class MyMemberInfoModel extends UsedByDependencyMemberInfoModel<PsiMember, PsiClass, MemberInfo> {
|
||||
|
||||
@@ -15,63 +15,17 @@
|
||||
*/
|
||||
package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.generation.OverrideImplementUtil;
|
||||
import com.intellij.codeInsight.intention.impl.CreateClassDialog;
|
||||
import com.intellij.codeInsight.intention.impl.CreateSubclassAction;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.search.searches.ClassInheritorsSearch;
|
||||
import com.intellij.psi.search.searches.FunctionalExpressionSearch;
|
||||
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.TypeConversionUtil;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.listeners.JavaRefactoringListenerManager;
|
||||
import com.intellij.refactoring.listeners.RefactoringEventData;
|
||||
import com.intellij.refactoring.listeners.impl.JavaRefactoringListenerManagerImpl;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
import com.intellij.refactoring.util.RefactoringUtil;
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfo;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
public class PushDownProcessor extends AbstractPushDownProcessor {
|
||||
|
||||
public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.memberPushDown.PushDownProcessor");
|
||||
|
||||
private final MemberInfo[] myMemberInfos;
|
||||
private PsiClass myClass;
|
||||
private final DocCommentPolicy myJavaDocPolicy;
|
||||
private CreateClassDialog myCreateClassDlg;
|
||||
|
||||
public PushDownProcessor(Project project,
|
||||
public PushDownProcessor(PsiClass aClass,
|
||||
MemberInfo[] memberInfos,
|
||||
PsiClass aClass,
|
||||
DocCommentPolicy javaDocPolicy) {
|
||||
super(project);
|
||||
myMemberInfos = memberInfos;
|
||||
myClass = aClass;
|
||||
myJavaDocPolicy = javaDocPolicy;
|
||||
super(aClass, memberInfos, javaDocPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,471 +33,9 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
return JavaPushDownHandler.REFACTORING_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected UsageViewDescriptor createUsageViewDescriptor(@NotNull UsageInfo[] usages) {
|
||||
return new PushDownUsageViewDescriptor<MemberInfo>(myClass, myMemberInfos);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Collection<? extends PsiElement> getElementsToWrite(@NotNull UsageViewDescriptor descriptor) {
|
||||
return Collections.singletonList(myClass);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected String getRefactoringId() {
|
||||
return "refactoring.push.down";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected RefactoringEventData getBeforeData() {
|
||||
RefactoringEventData data = new RefactoringEventData();
|
||||
data.addElement(myClass);
|
||||
data.addMembers(myMemberInfos, new Function<MemberInfo, PsiElement>() {
|
||||
@Override
|
||||
public PsiElement fun(MemberInfo info) {
|
||||
return info.getMember();
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected RefactoringEventData getAfterData(@NotNull UsageInfo[] usages) {
|
||||
final List<PsiElement> elements = new ArrayList<PsiElement>();
|
||||
for (UsageInfo usage : usages) {
|
||||
PsiElement element = usage.getElement();
|
||||
if (element instanceof PsiClass) {
|
||||
elements.add(element);
|
||||
}
|
||||
}
|
||||
RefactoringEventData data = new RefactoringEventData();
|
||||
data.addElements(elements);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected UsageInfo[] findUsages() {
|
||||
final PsiClass[] inheritors = ClassInheritorsSearch.search(myClass, false).toArray(PsiClass.EMPTY_ARRAY);
|
||||
final List<UsageInfo> usages = new ArrayList<UsageInfo>(inheritors.length);
|
||||
for (PsiClass inheritor : inheritors) {
|
||||
usages.add(new UsageInfo(inheritor));
|
||||
}
|
||||
|
||||
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(myClass);
|
||||
if (interfaceMethod != null && isMoved(interfaceMethod)) {
|
||||
FunctionalExpressionSearch.search(myClass).forEach(new Processor<PsiFunctionalExpression>() {
|
||||
@Override
|
||||
public boolean process(PsiFunctionalExpression expression) {
|
||||
usages.add(new UsageInfo(expression));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return usages.toArray(new UsageInfo[usages.size()]);
|
||||
}
|
||||
|
||||
private boolean isMoved(PsiMember member) {
|
||||
for (MemberInfo info : myMemberInfos) {
|
||||
if (member == info.getMember()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preprocessUsages(@NotNull final Ref<UsageInfo[]> refUsages) {
|
||||
final UsageInfo[] usagesIn = refUsages.get();
|
||||
final PushDownConflicts pushDownConflicts = new PushDownConflicts(myClass, myMemberInfos);
|
||||
pushDownConflicts.checkSourceClassConflicts();
|
||||
|
||||
if (usagesIn.length == 0) {
|
||||
Boolean answered = preprocessNoInheritorsFound(usagesIn, pushDownConflicts);
|
||||
if (answered != null) {
|
||||
return answered;
|
||||
}
|
||||
}
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (UsageInfo usage : usagesIn) {
|
||||
pushDownConflicts.checkTargetClassConflicts(usage.getElement(), usagesIn.length > 1, usage.getElement());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return showConflicts(pushDownConflicts.getConflicts(), usagesIn);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Boolean preprocessNoInheritorsFound(UsageInfo[] usagesIn, PushDownConflicts pushDownConflicts) {
|
||||
if (myClass.isEnum() || myClass.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
if (Messages.showOkCancelDialog((myClass.isEnum() ? "Enum " + myClass.getQualifiedName() + " doesn't have constants to inline to. " : "Final class " + myClass.getQualifiedName() + "does not have inheritors. ") +
|
||||
"Pushing members down will result in them being deleted. " +
|
||||
"Would you like to proceed?", JavaPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon()) != Messages.OK) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
String noInheritors = myClass.isInterface() ?
|
||||
RefactoringBundle.message("interface.0.does.not.have.inheritors", myClass.getQualifiedName()) :
|
||||
RefactoringBundle.message("class.0.does.not.have.inheritors", myClass.getQualifiedName());
|
||||
final String message = noInheritors + "\n" + RefactoringBundle.message("push.down.will.delete.members");
|
||||
final int answer = Messages.showYesNoCancelDialog(message, JavaPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon());
|
||||
if (answer == Messages.YES) {
|
||||
myCreateClassDlg = CreateSubclassAction.chooseSubclassToCreate(myClass);
|
||||
if (myCreateClassDlg != null) {
|
||||
pushDownConflicts.checkTargetClassConflicts(null, false, myCreateClassDlg.getTargetDirectory());
|
||||
return showConflicts(pushDownConflicts.getConflicts(), usagesIn);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (answer != Messages.NO) return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshElements(@NotNull PsiElement[] elements) {
|
||||
if(elements.length == 1 && elements[0] instanceof PsiClass) {
|
||||
myClass = (PsiClass) elements[0];
|
||||
}
|
||||
else {
|
||||
LOG.assertTrue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(@NotNull UsageInfo[] usages) {
|
||||
try {
|
||||
encodeRefs();
|
||||
if (myCreateClassDlg != null) { //usages.length == 0
|
||||
final PsiClass psiClass =
|
||||
CreateSubclassAction.createSubclass(myClass, myCreateClassDlg.getTargetDirectory(), myCreateClassDlg.getClassName());
|
||||
if (psiClass != null) {
|
||||
pushDownToClass(psiClass);
|
||||
}
|
||||
}
|
||||
for (UsageInfo usage : usages) {
|
||||
if (usage.getElement() instanceof PsiClass) {
|
||||
final PsiClass targetClass = (PsiClass)usage.getElement();
|
||||
pushDownToClass(targetClass);
|
||||
}
|
||||
}
|
||||
removeFromTargetClass();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Key<Boolean> REMOVE_QUALIFIER_KEY = Key.create("REMOVE_QUALIFIER_KEY");
|
||||
private static final Key<PsiClass> REPLACE_QUALIFIER_KEY = Key.create("REPLACE_QUALIFIER_KEY");
|
||||
|
||||
protected void encodeRefs() {
|
||||
final Set<PsiMember> movedMembers = new HashSet<PsiMember>();
|
||||
for (MemberInfo memberInfo : myMemberInfos) {
|
||||
movedMembers.add(memberInfo.getMember());
|
||||
}
|
||||
|
||||
for (MemberInfo memberInfo : myMemberInfos) {
|
||||
final PsiMember member = memberInfo.getMember();
|
||||
member.accept(new JavaRecursiveElementVisitor() {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
encodeRef(expression, movedMembers, expression);
|
||||
super.visitReferenceExpression(expression);
|
||||
}
|
||||
|
||||
@Override public void visitNewExpression(PsiNewExpression expression) {
|
||||
final PsiJavaCodeReferenceElement classReference = expression.getClassReference();
|
||||
if (classReference != null) {
|
||||
encodeRef(classReference, movedMembers, expression);
|
||||
}
|
||||
super.visitNewExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeElement(final PsiTypeElement type) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = type.getInnermostComponentReferenceElement();
|
||||
if (referenceElement != null) {
|
||||
encodeRef(referenceElement, movedMembers, type);
|
||||
}
|
||||
super.visitTypeElement(type);
|
||||
}
|
||||
});
|
||||
ChangeContextUtil.encodeContextInfo(member, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeRef(final PsiJavaCodeReferenceElement expression, final Set<PsiMember> movedMembers, final PsiElement toPut) {
|
||||
final PsiElement resolved = expression.resolve();
|
||||
if (resolved == null) return;
|
||||
final PsiElement qualifier = expression.getQualifier();
|
||||
for (PsiMember movedMember : movedMembers) {
|
||||
if (movedMember.equals(resolved)) {
|
||||
if (qualifier == null) {
|
||||
toPut.putCopyableUserData(REMOVE_QUALIFIER_KEY, Boolean.TRUE);
|
||||
} else {
|
||||
if (qualifier instanceof PsiJavaCodeReferenceElement &&
|
||||
((PsiJavaCodeReferenceElement)qualifier).isReferenceTo(myClass)) {
|
||||
toPut.putCopyableUserData(REPLACE_QUALIFIER_KEY, myClass);
|
||||
}
|
||||
}
|
||||
} else if (movedMember instanceof PsiClass && PsiTreeUtil.getParentOfType(resolved, PsiClass.class, false) == movedMember) {
|
||||
if (qualifier instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)qualifier).isReferenceTo(movedMember)) {
|
||||
toPut.putCopyableUserData(REPLACE_QUALIFIER_KEY, (PsiClass)movedMember);
|
||||
}
|
||||
} else {
|
||||
if (qualifier instanceof PsiThisExpression) {
|
||||
final PsiJavaCodeReferenceElement qElement = ((PsiThisExpression)qualifier).getQualifier();
|
||||
if (qElement != null && qElement.isReferenceTo(myClass)) {
|
||||
toPut.putCopyableUserData(REPLACE_QUALIFIER_KEY, myClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeRefs(final PsiMember member, final PsiClass targetClass) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(myProject).getElementFactory();
|
||||
member.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
decodeRef(expression, factory, targetClass, expression);
|
||||
super.visitReferenceExpression(expression);
|
||||
}
|
||||
|
||||
@Override public void visitNewExpression(PsiNewExpression expression) {
|
||||
final PsiJavaCodeReferenceElement classReference = expression.getClassReference();
|
||||
if (classReference != null) decodeRef(classReference, factory, targetClass, expression);
|
||||
super.visitNewExpression(expression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeElement(final PsiTypeElement type) {
|
||||
final PsiJavaCodeReferenceElement referenceElement = type.getInnermostComponentReferenceElement();
|
||||
if (referenceElement != null) decodeRef(referenceElement, factory, targetClass, type);
|
||||
super.visitTypeElement(type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void decodeRef(final PsiJavaCodeReferenceElement ref,
|
||||
final PsiElementFactory factory,
|
||||
final PsiClass targetClass,
|
||||
final PsiElement toGet) {
|
||||
try {
|
||||
if (toGet.getCopyableUserData(REMOVE_QUALIFIER_KEY) != null) {
|
||||
toGet.putCopyableUserData(REMOVE_QUALIFIER_KEY, null);
|
||||
final PsiElement qualifier = ref.getQualifier();
|
||||
if (qualifier != null) qualifier.delete();
|
||||
}
|
||||
else {
|
||||
PsiClass psiClass = toGet.getCopyableUserData(REPLACE_QUALIFIER_KEY);
|
||||
if (psiClass != null) {
|
||||
toGet.putCopyableUserData(REPLACE_QUALIFIER_KEY, null);
|
||||
PsiElement qualifier = ref.getQualifier();
|
||||
if (qualifier != null) {
|
||||
|
||||
if (psiClass == myClass) {
|
||||
psiClass = targetClass;
|
||||
} else if (psiClass.getContainingClass() == myClass) {
|
||||
psiClass = targetClass.findInnerClassByName(psiClass.getName(), false);
|
||||
LOG.assertTrue(psiClass != null);
|
||||
}
|
||||
|
||||
if (!(qualifier instanceof PsiThisExpression) && ref instanceof PsiReferenceExpression) {
|
||||
((PsiReferenceExpression)ref).setQualifierExpression(factory.createReferenceExpression(psiClass));
|
||||
}
|
||||
else {
|
||||
if (qualifier instanceof PsiThisExpression) {
|
||||
qualifier = ((PsiThisExpression)qualifier).getQualifier();
|
||||
}
|
||||
qualifier.replace(factory.createReferenceElementByType(factory.createType(psiClass)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeFromTargetClass() throws IncorrectOperationException {
|
||||
for (MemberInfo memberInfo : myMemberInfos) {
|
||||
final PsiElement member = memberInfo.getMember();
|
||||
|
||||
if (member instanceof PsiField) {
|
||||
member.delete();
|
||||
}
|
||||
else if (member instanceof PsiMethod) {
|
||||
if (memberInfo.isToAbstract()) {
|
||||
final PsiMethod method = (PsiMethod)member;
|
||||
if (method.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
PsiUtil.setModifierProperty(method, PsiModifier.PROTECTED, true);
|
||||
}
|
||||
RefactoringUtil.makeMethodAbstract(myClass, method);
|
||||
myJavaDocPolicy.processOldJavaDoc(method.getDocComment());
|
||||
}
|
||||
else {
|
||||
member.delete();
|
||||
}
|
||||
}
|
||||
else if (member instanceof PsiClass) {
|
||||
if (Boolean.FALSE.equals(memberInfo.getOverrides())) {
|
||||
RefactoringUtil.removeFromReferenceList(myClass.getImplementsList(), (PsiClass)member);
|
||||
}
|
||||
else {
|
||||
member.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void pushDownToClass(PsiClass targetClass) throws IncorrectOperationException {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory();
|
||||
final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(myClass, targetClass, PsiSubstitutor.EMPTY);
|
||||
for (MemberInfo memberInfo : myMemberInfos) {
|
||||
PsiMember member = memberInfo.getMember();
|
||||
final List<PsiReference> refsToRebind = new ArrayList<PsiReference>();
|
||||
final PsiModifierList list = member.getModifierList();
|
||||
LOG.assertTrue(list != null);
|
||||
if (list.hasModifierProperty(PsiModifier.STATIC)) {
|
||||
for (final PsiReference reference : ReferencesSearch.search(member)) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
final PsiExpression qualifierExpression = ((PsiReferenceExpression)element).getQualifierExpression();
|
||||
if (qualifierExpression instanceof PsiReferenceExpression && !(((PsiReferenceExpression)qualifierExpression).resolve() instanceof PsiClass)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
refsToRebind.add(reference);
|
||||
}
|
||||
}
|
||||
member = (PsiMember)member.copy();
|
||||
RefactoringUtil.replaceMovedMemberTypeParameters(member, PsiUtil.typeParametersIterable(myClass), substitutor, factory);
|
||||
PsiMember newMember = null;
|
||||
if (member instanceof PsiField) {
|
||||
((PsiField)member).normalizeDeclaration();
|
||||
if (myClass.isInterface() && !targetClass.isInterface()) {
|
||||
PsiUtil.setModifierProperty(member, PsiModifier.PUBLIC, true);
|
||||
PsiUtil.setModifierProperty(member, PsiModifier.STATIC, true);
|
||||
PsiUtil.setModifierProperty(member, PsiModifier.FINAL, true);
|
||||
}
|
||||
newMember = (PsiMember)targetClass.add(member);
|
||||
}
|
||||
else if (member instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod)member;
|
||||
PsiMethod methodBySignature = MethodSignatureUtil.findMethodBySuperSignature(targetClass, method.getSignature(substitutor), false);
|
||||
if (methodBySignature == null) {
|
||||
newMember = (PsiMethod)targetClass.add(method);
|
||||
if (myClass.isInterface()) {
|
||||
if (!targetClass.isInterface()) {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.PUBLIC, true);
|
||||
if (newMember.hasModifierProperty(PsiModifier.DEFAULT)) {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.DEFAULT, false);
|
||||
}
|
||||
else {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.ABSTRACT, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (memberInfo.isToAbstract()) {
|
||||
if (newMember.hasModifierProperty(PsiModifier.PRIVATE)) {
|
||||
PsiUtil.setModifierProperty(newMember, PsiModifier.PROTECTED, true);
|
||||
}
|
||||
myJavaDocPolicy.processNewJavaDoc(((PsiMethod)newMember).getDocComment());
|
||||
}
|
||||
if (memberInfo.isToAbstract()) {
|
||||
OverrideImplementUtil.annotateOnOverrideImplement((PsiMethod)newMember, targetClass, (PsiMethod)memberInfo.getMember());
|
||||
}
|
||||
}
|
||||
else { //abstract method: remove @Override
|
||||
final PsiAnnotation annotation = AnnotationUtil.findAnnotation(methodBySignature, "java.lang.Override");
|
||||
if (annotation != null && !leaveOverrideAnnotation(substitutor, method)) {
|
||||
annotation.delete();
|
||||
}
|
||||
final PsiDocComment oldDocComment = method.getDocComment();
|
||||
if (oldDocComment != null) {
|
||||
final PsiDocComment docComment = methodBySignature.getDocComment();
|
||||
final int policy = myJavaDocPolicy.getJavaDocPolicy();
|
||||
if (policy == DocCommentPolicy.COPY || policy == DocCommentPolicy.MOVE) {
|
||||
if (docComment != null) {
|
||||
docComment.replace(oldDocComment);
|
||||
}
|
||||
else {
|
||||
methodBySignature.getParent().addBefore(oldDocComment, methodBySignature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (member instanceof PsiClass) {
|
||||
if (Boolean.FALSE.equals(memberInfo.getOverrides())) {
|
||||
final PsiClass aClass = (PsiClass)memberInfo.getMember();
|
||||
PsiClassType classType = null;
|
||||
if (!targetClass.isInheritor(aClass, false)) {
|
||||
final PsiClassType[] types = memberInfo.getSourceReferenceList().getReferencedTypes();
|
||||
for (PsiClassType type : types) {
|
||||
if (type.resolve() == aClass) {
|
||||
classType = (PsiClassType)substitutor.substitute(type);
|
||||
}
|
||||
}
|
||||
PsiJavaCodeReferenceElement classRef = classType != null ? factory.createReferenceElementByType(classType) : factory.createClassReferenceElement(aClass);
|
||||
if (aClass.isInterface()) {
|
||||
targetClass.getImplementsList().add(classRef);
|
||||
} else {
|
||||
targetClass.getExtendsList().add(classRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
newMember = (PsiMember)targetClass.add(member);
|
||||
}
|
||||
}
|
||||
|
||||
if (newMember != null) {
|
||||
decodeRefs(newMember, targetClass);
|
||||
//rebind imports first
|
||||
Collections.sort(refsToRebind, Comparator.comparing(PsiReference::getElement, PsiUtil.BY_POSITION));
|
||||
for (PsiReference psiReference : refsToRebind) {
|
||||
JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(psiReference.bindToElement(newMember));
|
||||
}
|
||||
final JavaRefactoringListenerManager listenerManager = JavaRefactoringListenerManager.getInstance(newMember.getProject());
|
||||
((JavaRefactoringListenerManagerImpl)listenerManager).fireMemberMoved(myClass, newMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean leaveOverrideAnnotation(PsiSubstitutor substitutor, PsiMethod method) {
|
||||
final PsiMethod methodBySignature = MethodSignatureUtil.findMethodBySignature(myClass, method.getSignature(substitutor), false);
|
||||
if (methodBySignature == null) return false;
|
||||
final PsiMethod[] superMethods = methodBySignature.findDeepestSuperMethods();
|
||||
if (superMethods.length == 0) return false;
|
||||
final boolean is15 = !PsiUtil.isLanguageLevel6OrHigher(methodBySignature);
|
||||
if (is15) {
|
||||
for (PsiMethod psiMethod : superMethods) {
|
||||
final PsiClass aClass = psiMethod.getContainingClass();
|
||||
if (aClass != null && aClass.isInterface()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ public class PushDownMultifileTest extends MultiFileTestCase {
|
||||
memberInfo.setChecked(true);
|
||||
membersToMove[0] = memberInfo;
|
||||
|
||||
new PushDownProcessor(getProject(), membersToMove, srcClass, new DocCommentPolicy(DocCommentPolicy.ASIS)).run();
|
||||
new PushDownProcessor(srcClass, membersToMove, new DocCommentPolicy(DocCommentPolicy.ASIS)).run();
|
||||
|
||||
|
||||
//LocalFileSystem.getInstance().refresh(false);
|
||||
@@ -120,7 +120,7 @@ public class PushDownMultifileTest extends MultiFileTestCase {
|
||||
memberInfo.setChecked(true);
|
||||
membersToMove[0] = memberInfo;
|
||||
|
||||
new PushDownProcessor(getProject(), membersToMove, srcClass, new DocCommentPolicy(DocCommentPolicy.ASIS)).run();
|
||||
new PushDownProcessor(srcClass, membersToMove, new DocCommentPolicy(DocCommentPolicy.ASIS)).run();
|
||||
|
||||
|
||||
//LocalFileSystem.getInstance().refresh(false);
|
||||
|
||||
@@ -105,7 +105,7 @@ public class PushDownTest extends LightRefactoringTestCase {
|
||||
memberInfo.setChecked(true);
|
||||
membersToMove.add(memberInfo);
|
||||
|
||||
new PushDownProcessor(getProject(), membersToMove.toArray(new MemberInfo[membersToMove.size()]), currentClass,
|
||||
new PushDownProcessor(currentClass, membersToMove.toArray(new MemberInfo[membersToMove.size()]),
|
||||
new DocCommentPolicy(DocCommentPolicy.ASIS)) {
|
||||
@Override
|
||||
protected boolean showConflicts(@NotNull MultiMap<PsiElement, String> conflicts, UsageInfo[] usages) {
|
||||
@@ -136,7 +136,7 @@ public class PushDownTest extends LightRefactoringTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
new PushDownProcessor(getProject(), members.toArray(new MemberInfo[members.size()]), currentClass,
|
||||
new PushDownProcessor(currentClass, members.toArray(new MemberInfo[members.size()]),
|
||||
new DocCommentPolicy(DocCommentPolicy.ASIS)) {
|
||||
@Override
|
||||
protected boolean showConflicts(@NotNull MultiMap<PsiElement, String> conflicts, UsageInfo[] usages) {
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
||||
import com.intellij.refactoring.listeners.RefactoringEventData;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractPushDownProcessor extends BaseRefactoringProcessor {
|
||||
private static final Logger LOG = Logger.getInstance("#" + AbstractPushDownProcessor.class.getName());
|
||||
|
||||
private NewSubClassData mySubClassData;
|
||||
private PushDownDelegate myDelegate;
|
||||
private PushDownData myPushDownData;
|
||||
|
||||
public AbstractPushDownProcessor(@NotNull PsiElement sourceClass,
|
||||
@NotNull MemberInfoBase<? extends PsiElement>[] memberInfos,
|
||||
@NotNull DocCommentPolicy javaDocPolicy) {
|
||||
super(sourceClass.getProject());
|
||||
myDelegate = PushDownDelegate.findDelegate(sourceClass);
|
||||
LOG.assertTrue(myDelegate != null);
|
||||
myPushDownData = new PushDownData(sourceClass, memberInfos, javaDocPolicy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected UsageViewDescriptor createUsageViewDescriptor(@NotNull UsageInfo[] usages) {
|
||||
return new PushDownUsageViewDescriptor(myPushDownData.getSourceClass(), myPushDownData.getMembersToMove());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Collection<? extends PsiElement> getElementsToWrite(@NotNull UsageViewDescriptor descriptor) {
|
||||
return Collections.singletonList(myPushDownData.getSourceClass());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected RefactoringEventData getBeforeData() {
|
||||
RefactoringEventData data = new RefactoringEventData();
|
||||
data.addElement(myPushDownData.getSourceClass());
|
||||
data.addMembers(myPushDownData.getMembersToMove(), new Function<MemberInfoBase<? extends PsiElement>, PsiElement>() {
|
||||
@Override
|
||||
public PsiElement fun(MemberInfoBase<? extends PsiElement> info) {
|
||||
return info.getMember();
|
||||
}
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected RefactoringEventData getAfterData(@NotNull UsageInfo[] usages) {
|
||||
final List<PsiElement> elements = new ArrayList<PsiElement>();
|
||||
for (UsageInfo usage : usages) {
|
||||
elements.add(usage.getElement());
|
||||
}
|
||||
RefactoringEventData data = new RefactoringEventData();
|
||||
data.addElements(elements);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
protected UsageInfo[] findUsages() {
|
||||
final List<PsiElement> inheritors = myDelegate.findInheritors(myPushDownData);
|
||||
return ContainerUtil.map2Array(inheritors, UsageInfo.EMPTY_ARRAY, myDelegate::createUsageInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preprocessUsages(@NotNull final Ref<UsageInfo[]> refUsages) {
|
||||
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
|
||||
myDelegate.checkSourceClassConflicts(myPushDownData, conflicts);
|
||||
final UsageInfo[] usagesIn = refUsages.get();
|
||||
if (usagesIn.length == 0) {
|
||||
mySubClassData = myDelegate.preprocessNoInheritorsFound(myPushDownData.getSourceClass(), getCommandName());
|
||||
if (mySubClassData == NewSubClassData.EMPTY) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mySubClassData != null) {
|
||||
myDelegate.checkTargetClassConflicts(null, myPushDownData, false, mySubClassData.getContext(), conflicts);
|
||||
}
|
||||
else {
|
||||
for (UsageInfo usage : usagesIn) {
|
||||
final PsiElement element = usage.getElement();
|
||||
if (element != null) {
|
||||
final PushDownDelegate delegate = PushDownDelegate.findDelegateForTarget(myPushDownData.getSourceClass(), element);
|
||||
if (delegate != null) {
|
||||
delegate.checkTargetClassConflicts(element, myPushDownData, usagesIn.length > 1, element, conflicts);
|
||||
}
|
||||
else {
|
||||
conflicts.putValue(element, "Not supported source/target pair detected");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(runnable, RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return showConflicts(conflicts, usagesIn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshElements(@NotNull PsiElement[] elements) {
|
||||
if(elements.length == 1) {
|
||||
myPushDownData.setSourceClass(elements[0]);
|
||||
}
|
||||
else {
|
||||
LOG.assertTrue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(@NotNull UsageInfo[] usages) {
|
||||
try {
|
||||
myDelegate.prepareToPush(myPushDownData);
|
||||
final PsiElement sourceClass = myPushDownData.getSourceClass();
|
||||
if (mySubClassData != null) {
|
||||
final PsiElement subClass = myDelegate.createSubClass(sourceClass, mySubClassData);
|
||||
if (subClass != null) {
|
||||
myDelegate.pushDownToClass(subClass, myPushDownData);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (UsageInfo usage : usages) {
|
||||
final PsiElement element = usage.getElement();
|
||||
if (element != null) {
|
||||
final PushDownDelegate targetDelegate = PushDownDelegate.findDelegateForTarget(sourceClass, element);
|
||||
if (targetDelegate != null) {
|
||||
targetDelegate.pushDownToClass(element, myPushDownData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
myDelegate.removeFromSourceClass(myPushDownData);
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void pushDownToDedicatedClass(PsiElement currentInheritor) {
|
||||
myDelegate.prepareToPush(myPushDownData);
|
||||
myDelegate.pushDownToClass(currentInheritor, myPushDownData);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
|
||||
public class NewSubClassData {
|
||||
public static final NewSubClassData EMPTY = new NewSubClassData(null, null);
|
||||
|
||||
private PsiElement myContext;
|
||||
private String myNewClassName;
|
||||
|
||||
public NewSubClassData(PsiElement context, String newClassName) {
|
||||
myContext = context;
|
||||
myNewClassName = newClassName;
|
||||
}
|
||||
|
||||
protected PsiElement getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
||||
protected String getNewClassName() {
|
||||
return myNewClassName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
||||
import com.intellij.refactoring.util.DocCommentPolicy;
|
||||
|
||||
public class PushDownData {
|
||||
private PsiElement mySourceClass;
|
||||
private final MemberInfoBase<? extends PsiElement>[] myMembersToMove;
|
||||
private final DocCommentPolicy myCommentPolicy;
|
||||
|
||||
public PushDownData(PsiElement sourceClass,
|
||||
MemberInfoBase<? extends PsiElement>[] membersToMove,
|
||||
DocCommentPolicy commentPolicy) {
|
||||
mySourceClass = sourceClass;
|
||||
myMembersToMove = membersToMove;
|
||||
myCommentPolicy = commentPolicy;
|
||||
}
|
||||
|
||||
public PsiElement getSourceClass() {
|
||||
return mySourceClass;
|
||||
}
|
||||
|
||||
public MemberInfoBase<? extends PsiElement>[] getMembersToMove() {
|
||||
return myMembersToMove;
|
||||
}
|
||||
|
||||
public DocCommentPolicy getCommentPolicy() {
|
||||
return myCommentPolicy;
|
||||
}
|
||||
|
||||
public void setSourceClass(PsiElement sourceClass) {
|
||||
mySourceClass = sourceClass;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.lang.findUsages.DescriptiveNameUtil;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class PushDownDelegate {
|
||||
public static final LanguageExtension<PushDownDelegate> EP_NAME = new LanguageExtension<PushDownDelegate>("com.intellij.refactoring.pushDown");
|
||||
|
||||
@Nullable
|
||||
protected static PushDownDelegate findDelegate(@NotNull PsiElement sourceClass) {
|
||||
return EP_NAME.forLanguage(sourceClass.getLanguage());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static PushDownDelegate findDelegateForTarget(@NotNull PsiElement sourceClass, @NotNull PsiElement targetClass) {
|
||||
for (PushDownDelegate delegate : EP_NAME.allForLanguage(targetClass.getLanguage())) {
|
||||
if (delegate.isApplicableForSource(sourceClass)) {
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if delegate can process pushed members from the sourceClass.
|
||||
* It is used to find appropriate delegate to process pushing from source class to target {@link #findDelegateForTarget(PsiElement, PsiElement)}.
|
||||
*
|
||||
* Implementations are supposed to override this method when overriding default behaviour for the language,
|
||||
* e.g. pushing members from groovy class to java, groovy could provide additional delegate which inherits delegate for java and accepts groovy sources.
|
||||
* Methods to process target class should be updated to cope with source of another language (e.g. calling super on PushDownData translated to java):
|
||||
* {@link #checkTargetClassConflicts(PsiElement, PushDownData, boolean, PsiElement, MultiMap)},
|
||||
* {@link #pushDownToClass(PsiElement, PushDownData)}
|
||||
*/
|
||||
protected abstract boolean isApplicableForSource(@NotNull PsiElement sourceClass);
|
||||
|
||||
/**
|
||||
* Find classes to push members down.
|
||||
*/
|
||||
protected abstract List<PsiElement> findInheritors(PushDownData pushDownData);
|
||||
|
||||
protected UsageInfo createUsageInfo(PsiElement element) {
|
||||
return new UsageInfo(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect conflicts inside sourceClass assuming members would be removed,
|
||||
* e.g. check if members remaining in source class do not depend on moved members
|
||||
*/
|
||||
protected abstract void checkSourceClassConflicts(PushDownData pushDownData, MultiMap<PsiElement, String> conflicts);
|
||||
|
||||
/**
|
||||
* Collect conflicts inside targetClass assuming methods would be pushed,
|
||||
* e.g. check if target class already has field with the same name, some references types
|
||||
* won't be accessible anymore, etc
|
||||
*/
|
||||
protected abstract void checkTargetClassConflicts(PsiElement targetClass,
|
||||
PushDownData pushDownData,
|
||||
boolean checkStatic,
|
||||
PsiElement context,
|
||||
MultiMap<PsiElement, String> conflicts);
|
||||
|
||||
/**
|
||||
* Could be used e.g. to encode mutual references between moved members
|
||||
*/
|
||||
protected void prepareToPush(PushDownData pushDownData) {}
|
||||
|
||||
/**
|
||||
* Push members to the target class adjusting visibility, comments according to the policy, etc
|
||||
*/
|
||||
protected abstract void pushDownToClass(PsiElement targetClass, PushDownData pushDownData);
|
||||
|
||||
/**
|
||||
* Remove members from the source class according to the abstract flag.
|
||||
*/
|
||||
protected abstract void removeFromSourceClass(PushDownData pushDownData);
|
||||
|
||||
/**
|
||||
* Called if no inheritors were found in {@link #findInheritors(PushDownData)}. Should warn that members would be deleted and
|
||||
* suggest to create new target class if applicable
|
||||
*
|
||||
* @return NewSubClassData.EMPTY if refactoring should be aborted
|
||||
* null to proceed without inheritors (members would be deleted from the source class and not added to the target)
|
||||
* new NewSubClassData(context, name) if new inheritor should be created with {@link #createSubClass(PsiElement, NewSubClassData)}
|
||||
*/
|
||||
protected NewSubClassData preprocessNoInheritorsFound(PsiElement sourceClass, String conflictDialogTitle) {
|
||||
final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", DescriptiveNameUtil.getDescriptiveName(sourceClass)) + "\n" +
|
||||
RefactoringBundle.message("push.down.will.delete.members");
|
||||
final int answer = Messages.showYesNoDialog(message, conflictDialogTitle, Messages.getWarningIcon());
|
||||
if (answer != Messages.YES) {
|
||||
return NewSubClassData.EMPTY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sub class with <code>subClassData.getNewClassName()</code> in the specified context if no inheritors were found
|
||||
*/
|
||||
@Nullable
|
||||
protected PsiElement createSubClass(PsiElement aClass, NewSubClassData subClassData) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -25,7 +25,7 @@ import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PushDownUsageViewDescriptor<M extends MemberInfoBase> implements UsageViewDescriptor {
|
||||
public class PushDownUsageViewDescriptor implements UsageViewDescriptor {
|
||||
private final PsiElement[] myMembers;
|
||||
private final String myProcessedElementsHeader;
|
||||
|
||||
@@ -33,10 +33,10 @@ public class PushDownUsageViewDescriptor<M extends MemberInfoBase> implements Us
|
||||
this(aClass, null);
|
||||
}
|
||||
|
||||
public PushDownUsageViewDescriptor(PsiElement aClass, M[] memberInfos) {
|
||||
myMembers = memberInfos != null ? ContainerUtil.map(memberInfos, new Function<M, PsiElement>() {
|
||||
public PushDownUsageViewDescriptor(PsiElement aClass, MemberInfoBase<? extends PsiElement>[] memberInfos) {
|
||||
myMembers = memberInfos != null ? ContainerUtil.map(memberInfos, new Function<MemberInfoBase<? extends PsiElement>, PsiElement>() {
|
||||
@Override
|
||||
public PsiElement fun(M info) {
|
||||
public PsiElement fun(MemberInfoBase<? extends PsiElement> info) {
|
||||
return info.getMember();
|
||||
}
|
||||
}, PsiElement.EMPTY_ARRAY) : new PsiElement[] {aClass};
|
||||
|
||||
@@ -784,7 +784,7 @@ rename.directory.command.name=Rename directory
|
||||
rename.directories.command.name=Rename directories
|
||||
target.destination.folder=Target &destination directory:
|
||||
move.method.is.not.supported.for.0=Move instance method is not supported for {0}
|
||||
detecting.possible.conflicts=Detecting possible conflicts...
|
||||
detecting.possible.conflicts=Detecting Possible Conflicts...
|
||||
path.completion.shortcut=Use {0} for path completion
|
||||
|
||||
rename.project.action.title=Rename Project...
|
||||
|
||||
@@ -373,6 +373,9 @@
|
||||
<extensionPoint name="refactoring.copyHandler" interface="com.intellij.refactoring.copy.CopyHandlerDelegate"/>
|
||||
<extensionPoint name="refactoring.moveHandler" interface="com.intellij.refactoring.move.MoveHandlerDelegate"/>
|
||||
<extensionPoint name="refactoring.invertBoolean" interface="com.intellij.refactoring.invertBoolean.InvertBooleanDelegate"/>
|
||||
<extensionPoint name="refactoring.pushDown" beanClass="com.intellij.lang.LanguageExtensionPoint">
|
||||
<with attribute="implementationClass" implements="com.intellij.refactoring.memberPushDown.PushDownDelegate"/>
|
||||
</extensionPoint>
|
||||
<extensionPoint name="refactoring.moveDirectoryWithClassesHelper"
|
||||
interface="com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesHelper"/>
|
||||
|
||||
|
||||
@@ -1724,6 +1724,7 @@
|
||||
<refactoring.moveInnerClassUsagesHandler language="JAVA" implementationClass="com.intellij.refactoring.move.moveInner.MoveInnerClassJavaUsagesHandler" id="java"/>
|
||||
<actionPromoter implementation="com.intellij.execution.testframework.TestTreeViewActionsPromoter"/>
|
||||
<refactoring.invertBoolean implementation="com.intellij.refactoring.invertBoolean.JavaInvertBooleanDelegate"/>
|
||||
<refactoring.pushDown language="JAVA" implementationClass="com.intellij.refactoring.memberPushDown.JavaPushDownDelegate" id="java"/>
|
||||
<library.javaSourceRootDetector implementation="com.intellij.openapi.roots.ui.configuration.JavaSourceRootDetector"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user