introduce parameter object: extract change signature part to start under the same progress

This commit is contained in:
Anna.Kozlova
2016-03-17 18:04:01 +01:00
parent 72fbe54d5a
commit f967d9f0d0
3 changed files with 142 additions and 124 deletions
@@ -38,6 +38,8 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.RefactorJBundle;
import com.intellij.refactoring.changeSignature.ChangeInfo;
import com.intellij.refactoring.changeSignature.ChangeSignatureProcessorBase;
import com.intellij.refactoring.introduceparameterobject.usageInfo.*;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor;
@@ -77,6 +79,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
private final Set<PsiParameter> paramsNeedingGetters = new HashSet<PsiParameter>();
private final PsiClass existingClass;
private PsiMethod myExistingClassCompatibleConstructor;
private ChangeInfo myChangeInfo;
public IntroduceParameterObjectProcessor(String className,
String packageName,
@@ -179,7 +182,24 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
if (myUseExistingClass && existingClass != null) {
myExistingClassCompatibleConstructor = existingClassIsCompatible(existingClass, parameters);
}
findUsagesForMethod(method, usages, true);
final PsiCodeBlock body = method.getBody();
final String baseParameterName = StringUtil.decapitalize(className);
final String fixedParamName =
body != null
? JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(baseParameterName, body.getLBrace(), true)
: JavaCodeStyleManager.getInstance(myProject).propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER);
myChangeInfo =
new MergeMethodArguments(method, className, packageName, fixedParamName, paramsToMerge, typeParams, keepMethodAsDelegate,
myCreateInnerClass ? method.getContainingClass() : null).createChangeInfo();
for (UsageInfo info : ChangeSignatureProcessorBase.findUsages(myChangeInfo)) {
usages.add(new ChangeSignatureUsageWrapper(info));
}
findUsagesForMethod(method, usages, fixedParamName);
if (myUseExistingClass && existingClass != null && !(paramsNeedingGetters.isEmpty() && paramsNeedingSetters.isEmpty())) {
usages.add(new AppendAccessorsUsageInfo(existingClass, myGenerateAccessors, paramsNeedingGetters, paramsNeedingSetters, parameters));
@@ -187,7 +207,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
final PsiMethod[] overridingMethods = OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY);
for (PsiMethod siblingMethod : overridingMethods) {
findUsagesForMethod(siblingMethod, usages, false);
findUsagesForMethod(siblingMethod, usages, fixedParamName);
}
if (myNewVisibility != null) {
@@ -195,16 +215,7 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
}
}
private void findUsagesForMethod(PsiMethod overridingMethod, List<FixableUsageInfo> usages, boolean changeSignature) {
final PsiCodeBlock body = overridingMethod.getBody();
final String baseParameterName = StringUtil.decapitalize(className);
final String fixedParamName =
body != null
? JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(baseParameterName, body.getLBrace(), true)
: JavaCodeStyleManager.getInstance(myProject).propertyNameToVariableName(baseParameterName, VariableKind.PARAMETER);
usages.add(new MergeMethodArguments(overridingMethod, className, packageName, fixedParamName, paramsToMerge, typeParams, keepMethodAsDelegate, myCreateInnerClass ? method.getContainingClass() : null, changeSignature));
private void findUsagesForMethod(PsiMethod overridingMethod, List<FixableUsageInfo> usages, String fixedParamName) {
final ParamUsageVisitor visitor = new ParamUsageVisitor(overridingMethod, paramsToMerge);
overridingMethod.accept(visitor);
final Set<PsiReferenceExpression> values = visitor.getParameterUsages();
@@ -261,6 +272,13 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
}
}
}
List<UsageInfo> changeSignatureUsages = new ArrayList<>();
for (UsageInfo info : usageInfos) {
if (info instanceof ChangeSignatureUsageWrapper) {
changeSignatureUsages.add(((ChangeSignatureUsageWrapper)info).getInfo());
}
}
ChangeSignatureProcessorBase.doChangeSignature(myChangeInfo, changeSignatureUsages.toArray(new UsageInfo[changeSignatureUsages.size()]));
}
}
@@ -537,4 +555,20 @@ public class IntroduceParameterObjectProcessor extends FixableUsagesRefactoringP
}
}
private static class ChangeSignatureUsageWrapper extends FixableUsageInfo {
private final UsageInfo myInfo;
public ChangeSignatureUsageWrapper(UsageInfo info) {
super(info.getElement());
myInfo = info;
}
public UsageInfo getInfo() {
return myInfo;
}
@Override
public void fixUsage() throws IncorrectOperationException {}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
* 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.
@@ -13,32 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.introduceparameterobject.usageInfo;
package com.intellij.refactoring.introduceparameterobject;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor;
import com.intellij.refactoring.changeSignature.ChangeInfo;
import com.intellij.refactoring.changeSignature.JavaChangeInfoImpl;
import com.intellij.refactoring.changeSignature.ParameterInfoImpl;
import com.intellij.refactoring.util.FixableUsageInfo;
import com.intellij.refactoring.util.CanonicalTypes;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@SuppressWarnings({"MethodWithTooManyParameters"})
public class MergeMethodArguments extends FixableUsageInfo {
public class MergeMethodArguments {
private final PsiMethod method;
private final PsiClass myContainingClass;
private final boolean myChangeSignature;
private final boolean myKeepMethodAsDelegate;
private final List<PsiTypeParameter> typeParams;
private final String className;
@@ -53,50 +50,45 @@ public class MergeMethodArguments extends FixableUsageInfo {
String parameterName,
int[] paramsToMerge,
List<PsiTypeParameter> typeParams,
final boolean keepMethodAsDelegate, final PsiClass containingClass, boolean changeSignature) {
super(method);
final boolean keepMethodAsDelegate,
final PsiClass containingClass) {
this.paramsToMerge = paramsToMerge;
this.packageName = packageName;
this.className = className;
this.parameterName = parameterName;
this.method = method;
myContainingClass = containingClass;
myChangeSignature = changeSignature;
lastParamIsVararg = method.isVarArgs() && isParameterToMerge(method.getParameterList().getParametersCount() - 1);
myKeepMethodAsDelegate = keepMethodAsDelegate;
this.typeParams = new ArrayList<PsiTypeParameter>(typeParams);
}
public void fixUsage() throws IncorrectOperationException {
public ChangeInfo createChangeInfo() {
final Project project = method.getProject();
final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project);
final PsiMethod deepestSuperMethod = method.findDeepestSuperMethod();
final PsiClass psiClass;
PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
String packageName;
if (myContainingClass != null) {
psiClass = myContainingClass.findInnerClassByName(className, false);
}
else {
psiClass = psiFacade.findClass(StringUtil.getQualifiedName(packageName, className), GlobalSearchScope.allScope(project));
}
assert psiClass != null;
PsiSubstitutor subst = PsiSubstitutor.EMPTY;
if (deepestSuperMethod != null) {
final PsiClass parentClass = deepestSuperMethod.getContainingClass();
final PsiSubstitutor parentSubstitutor =
TypeConversionUtil.getSuperClassSubstitutor(parentClass, method.getContainingClass(), PsiSubstitutor.EMPTY);
for (int i1 = 0; i1 < psiClass.getTypeParameters().length; i1++) {
final PsiTypeParameter typeParameter = psiClass.getTypeParameters()[i1];
for (PsiTypeParameter parameter : parentClass.getTypeParameters()) {
if (Comparing.strEqual(typeParameter.getName(), parameter.getName())) {
subst = subst.put(typeParameter, parentSubstitutor.substitute(
new PsiImmediateClassType(parameter, PsiSubstitutor.EMPTY)));
break;
}
}
packageName = myContainingClass.getQualifiedName();
if (packageName == null) {
packageName = myContainingClass.getName();
}
}
else {
packageName = this.packageName;
}
String text = StringUtil.getQualifiedName(packageName, className);
if (!typeParams.isEmpty()) {
text += "<" + StringUtil.join(typeParams, new Function<PsiTypeParameter, String>() {
@Override
public String fun(PsiTypeParameter parameter) {
return parameter.getName();
}
}, ", ") + ">";
}
final PsiType classType = factory.createTypeFromText(text, method);
final List<ParameterInfoImpl> parametersInfo = new ArrayList<ParameterInfoImpl>();
final PsiClassType classType = JavaPsiFacade.getElementFactory(project).createType(psiClass, subst);
final ParameterInfoImpl mergedParamInfo = new ParameterInfoImpl(-1, parameterName, classType, null) {
@Override
@@ -117,33 +109,16 @@ public class MergeMethodArguments extends FixableUsageInfo {
}
parametersInfo.add(firstIncludedIdx == -1 ? 0 : firstIncludedIdx, mergedParamInfo);
final SmartPsiElementPointer<PsiMethod> meth = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(method);
final Runnable performChangeSignatureRunnable = new Runnable() {
@Override
public void run() {
final PsiMethod psiMethod = meth.getElement();
if (psiMethod == null) return;
if (myChangeSignature) {
final ChangeSignatureProcessor changeSignatureProcessor =
new ChangeSignatureProcessor(psiMethod.getProject(), psiMethod,
myKeepMethodAsDelegate, null, psiMethod.getName(),
psiMethod.getReturnType(),
parametersInfo.toArray(new ParameterInfoImpl[parametersInfo.size()]));
changeSignatureProcessor.run();
}
}
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
performChangeSignatureRunnable.run();
} else {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
CommandProcessor.getInstance().runUndoTransparentAction(performChangeSignatureRunnable);
}
});
}
PsiType returnType = method.getReturnType();
return new JavaChangeInfoImpl(VisibilityUtil.getVisibilityModifier(method.getModifierList()),
method,
method.getName(),
returnType != null ? CanonicalTypes.createTypeWrapper(returnType) : null,
parametersInfo.toArray(new ParameterInfoImpl[parametersInfo.size()]),
null,
myKeepMethodAsDelegate,
Collections.emptySet(),
Collections.emptySet());
}
private boolean isParameterToMerge(int index) {
@@ -71,23 +71,27 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces
@Override
@NotNull
protected UsageInfo[] findUsages() {
List<UsageInfo> infos = new ArrayList<UsageInfo>();
return findUsages(myChangeInfo);
}
@NotNull
public static UsageInfo[] findUsages(ChangeInfo changeInfo) {
List<UsageInfo> infos = new ArrayList<UsageInfo>();
final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
for (ChangeSignatureUsageProcessor processor : processors) {
ContainerUtil.addAll(infos, processor.findUsages(myChangeInfo));
ContainerUtil.addAll(infos, processor.findUsages(changeInfo));
}
infos = filterUsages(infos);
return infos.toArray(new UsageInfo[infos.size()]);
}
protected List<UsageInfo> filterUsages(List<UsageInfo> infos) {
protected static List<UsageInfo> filterUsages(List<UsageInfo> infos) {
Map<PsiElement, MoveRenameUsageInfo> moveRenameInfos = new HashMap<PsiElement, MoveRenameUsageInfo>();
Set<PsiElement> usedElements = new HashSet<PsiElement>();
List<UsageInfo> result = new ArrayList<UsageInfo>(infos.size() / 2);
for (UsageInfo info : infos) {
LOG.assertTrue(info != null, getClass());
LOG.assertTrue(info != null);
PsiElement element = info.getElement();
if (info instanceof MoveRenameUsageInfo) {
if (usedElements.contains(element)) continue;
@@ -139,14 +143,15 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces
@Override
protected void performRefactoring(@NotNull UsageInfo[] usages) {
RefactoringTransaction transaction = getTransaction();
final RefactoringElementListener elementListener = transaction == null ? null : transaction.getElementListener(myChangeInfo.getMethod());
final String fqn = CopyReferenceAction.elementToFqn(myChangeInfo.getMethod());
final ChangeInfo changeInfo = myChangeInfo;
final RefactoringElementListener elementListener = transaction == null ? null : transaction.getElementListener(changeInfo.getMethod());
final String fqn = CopyReferenceAction.elementToFqn(changeInfo.getMethod());
if (fqn != null) {
UndoableAction action = new BasicUndoableAction() {
@Override
public void undo() {
if (elementListener instanceof UndoRefactoringElementListener) {
((UndoRefactoringElementListener)elementListener).undoElementMovedOrRenamed(myChangeInfo.getMethod(), fqn);
((UndoRefactoringElementListener)elementListener).undoElementMovedOrRenamed(changeInfo.getMethod(), fqn);
}
}
@@ -157,44 +162,10 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces
UndoManager.getInstance(myProject).undoableActionPerformed(action);
}
try {
final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
final ResolveSnapshotProvider resolveSnapshotProvider = myChangeInfo.isParameterNamesChanged() ?
VariableInplaceRenamer.INSTANCE.forLanguage(myChangeInfo.getMethod().getLanguage()) : null;
final List<ResolveSnapshotProvider.ResolveSnapshot> snapshots = new ArrayList<ResolveSnapshotProvider.ResolveSnapshot>();
for (ChangeSignatureUsageProcessor processor : processors) {
if (resolveSnapshotProvider != null) {
processor.registerConflictResolvers(snapshots, resolveSnapshotProvider, usages, myChangeInfo);
}
}
for (UsageInfo usage : usages) {
for (ChangeSignatureUsageProcessor processor : processors) {
if (processor.processUsage(myChangeInfo, usage, true, usages)) break;
}
}
LOG.assertTrue(myChangeInfo.getMethod().isValid());
for (ChangeSignatureUsageProcessor processor : processors) {
if (processor.processPrimaryMethod(myChangeInfo)) break;
}
for (UsageInfo usage : usages) {
for (ChangeSignatureUsageProcessor processor : processors) {
if (processor.processUsage(myChangeInfo, usage, false, usages)) break;
}
}
if (!snapshots.isEmpty()) {
for (ParameterInfo parameterInfo : myChangeInfo.getNewParameters()) {
for (ResolveSnapshotProvider.ResolveSnapshot snapshot : snapshots) {
snapshot.apply(parameterInfo.getName());
}
}
}
final PsiElement method = myChangeInfo.getMethod();
doChangeSignature(changeInfo, usages);
final PsiElement method = changeInfo.getMethod();
LOG.assertTrue(method.isValid());
if (elementListener != null && myChangeInfo.isNameChanged()) {
if (elementListener != null && changeInfo.isNameChanged()) {
elementListener.elementRenamed(method);
}
}
@@ -203,6 +174,44 @@ public abstract class ChangeSignatureProcessorBase extends BaseRefactoringProces
}
}
public static void doChangeSignature(ChangeInfo changeInfo, @NotNull UsageInfo[] usages) {
final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
final ResolveSnapshotProvider resolveSnapshotProvider = changeInfo.isParameterNamesChanged() ?
VariableInplaceRenamer.INSTANCE.forLanguage(changeInfo.getMethod().getLanguage()) : null;
final List<ResolveSnapshotProvider.ResolveSnapshot> snapshots = new ArrayList<ResolveSnapshotProvider.ResolveSnapshot>();
for (ChangeSignatureUsageProcessor processor : processors) {
if (resolveSnapshotProvider != null) {
processor.registerConflictResolvers(snapshots, resolveSnapshotProvider, usages, changeInfo);
}
}
for (UsageInfo usage : usages) {
for (ChangeSignatureUsageProcessor processor : processors) {
if (processor.processUsage(changeInfo, usage, true, usages)) break;
}
}
LOG.assertTrue(changeInfo.getMethod().isValid());
for (ChangeSignatureUsageProcessor processor : processors) {
if (processor.processPrimaryMethod(changeInfo)) break;
}
for (UsageInfo usage : usages) {
for (ChangeSignatureUsageProcessor processor : processors) {
if (processor.processUsage(changeInfo, usage, false, usages)) break;
}
}
if (!snapshots.isEmpty()) {
for (ParameterInfo parameterInfo : changeInfo.getNewParameters()) {
for (ResolveSnapshotProvider.ResolveSnapshot snapshot : snapshots) {
snapshot.apply(parameterInfo.getName());
}
}
}
}
@Override
protected String getCommandName() {
return RefactoringBundle.message("changing.signature.of.0", DescriptiveNameUtil.getDescriptiveName(myChangeInfo.getMethod()));