IDEA-61244 introduce parameter refactoring for Groovy

This commit is contained in:
Maxim Medvedev
2011-03-10 11:49:24 +03:00
parent e49d82691a
commit 7afe8ae5f1
27 changed files with 1899 additions and 278 deletions
@@ -24,10 +24,13 @@
*/
package com.intellij.refactoring.introduceParameter;
import com.intellij.psi.*;
import com.intellij.psi.PsiElement;
class ChangedMethodCallInfo extends InternalUsageInfo {
ChangedMethodCallInfo(PsiElement e) {
/**
* recursive call usages inside method
*/
public class ChangedMethodCallInfo extends InternalUsageInfo {
public ChangedMethodCallInfo(PsiElement e) {
super(e);
}
}
@@ -0,0 +1,41 @@
/*
* Copyright 2000-2011 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.introduceParameter;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageExtension;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
/**
* @author Maxim.Medvedev
*/
public abstract class ExpressionConverter {
public static final LanguageExtension<ExpressionConverter> EP =
new LanguageExtension<ExpressionConverter>("com.intellij.expressionConverter");
protected abstract PsiElement convert(PsiElement expression, Project project);
@Nullable
public static PsiElement getExpression(PsiElement expression, Language language, Project project) {
if (expression.getLanguage() == language) return expression;
final ExpressionConverter converter = EP.forLanguage(language);
if (converter == null) return null;
return converter.convert(expression, project);
}
}
@@ -31,7 +31,7 @@ import com.intellij.usageView.UsageInfo;
* Usage of an expression in method
*/
public class InternalUsageInfo extends UsageInfo {
InternalUsageInfo(PsiElement e) {
public InternalUsageInfo(PsiElement e) {
super(e);
}
}
@@ -15,13 +15,13 @@
*/
package com.intellij.refactoring.introduceParameter;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiType;
import gnu.trove.TIntArrayList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface IntroduceParameterData {
@NotNull
@@ -32,27 +32,35 @@ public interface IntroduceParameterData {
@NotNull
PsiMethod getMethodToSearchFor();
PsiExpression getParameterInitializer();
PsiExpression getExpressionToSearch();
PsiLocalVariable getLocalVariable();
boolean isRemoveLocalVariable();
ExpressionWrapper getParameterInitializer();
@NotNull
String getParameterName();
boolean isReplaceAllOccurences();
/**
* @see com.intellij.refactoring.IntroduceParameterRefactoring
*/
int getReplaceFieldsWithGetters();
boolean isDeclareFinal();
boolean isGenerateDelegate();
@NotNull
PsiType getForcedType();
@NotNull
TIntArrayList getParametersToRemove();
interface ExpressionWrapper<RealExpression extends PsiElement> {
@NotNull
String getText();
@Nullable
PsiType getType();
@NotNull
RealExpression getExpression();
}
}
@@ -78,6 +78,7 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
private PsiType myForcedType;
private final TIntArrayList myParametersToRemove;
private final PsiManager myManager;
private JavaExpressionWrapper myInitializerWrapper;
/**
* if expressionToSearch is null, search for localVariable
@@ -114,12 +115,15 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
myManager = PsiManager.getInstance(project);
myParametersToRemove = parametersToRemove;
myInitializerWrapper = expressionToSearch == null ? null : new JavaExpressionWrapper(expressionToSearch);
}
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
return new IntroduceParameterViewDescriptor(myMethodToSearchFor);
}
@NotNull
public PsiType getForcedType() {
return myForcedType;
}
@@ -160,7 +164,7 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
else if (ref instanceof PsiClass) {
result.add(new NoConstructorClassUsageInfo((PsiClass)ref));
}
else if (!insideMethodToBeReplaced(ref)) {
else if (!IntroduceParameterUtil.insideMethodToBeReplaced(ref, myMethodToReplaceIn)) {
result.add(new ExternalUsageInfo(ref));
}
else {
@@ -256,7 +260,7 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
final Set<PsiElement> result = collector.myResult;
if (!result.isEmpty()) {
for (final UsageInfo usageInfo : usageArray) {
if (usageInfo instanceof ExternalUsageInfo && isMethodUsage(usageInfo)) {
if (usageInfo instanceof ExternalUsageInfo && IntroduceParameterUtil.isMethodUsage(usageInfo)) {
final PsiElement place = usageInfo.getElement();
for (PsiElement element : result) {
if (element instanceof PsiField && myReplaceFieldsWithGetters != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE) { //check getter access instead
@@ -283,13 +287,6 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
}
}
private static boolean isMethodUsage(UsageInfo usageInfo) {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (processor.isMethodUsage(usageInfo)) return true;
}
return false;
}
public static class AnySupers extends JavaRecursiveElementWalkingVisitor {
private boolean myResult = false;
@Override public void visitSuperExpression(PsiSuperExpression expression) {
@@ -331,20 +328,6 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
}
}
private boolean insideMethodToBeReplaced(PsiElement methodUsage) {
PsiElement parent = methodUsage.getParent();
while(parent != null) {
if (parent.equals(myMethodToReplaceIn)) {
return true;
}
parent = parent.getParent();
}
return false;
}
protected void refreshElements(PsiElement[] elements) {
}
protected void performRefactoring(UsageInfo[] usages) {
try {
PsiElementFactory factory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
@@ -360,29 +343,11 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
myParameterInitializer = RefactoringUtil.convertInitializerToNormalExpression(myParameterInitializer, initializerType);
}
myInitializerWrapper = new JavaExpressionWrapper(myParameterInitializer);
// Changing external occurences (the tricky part)
for (UsageInfo usage : usages) {
if (!(usage instanceof InternalUsageInfo)) {
if (usage instanceof DefaultConstructorImplicitUsageInfo) {
addSuperCall(usage, usages);
}
else if (usage instanceof NoConstructorClassUsageInfo) {
addDefaultConstructor(usage, usages);
}
else {
PsiElement element = usage.getElement();
if (element instanceof PsiMethod) {
if (!myManager.areElementsEquivalent(element, myMethodToReplaceIn)) {
changeMethodSignatureAndResolveFieldConflicts(usage, usages);
}
}
else if (!myGenerateDelegate) {
changeExternalUsage(usage, usages);
}
}
}
}
IntroduceParameterUtil.processUsages(usages, this);
if (myGenerateDelegate) {
generateDelegate(myMethodToReplaceIn);
@@ -401,9 +366,9 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
// (signature of myMethodToReplaceIn will be either changed now or have already been changed)
LOG.assertTrue(initializerType.isValid());
final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(myParameterName, myMethodToReplaceIn.getBody());
changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethodToReplaceIn), usages);
IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethodToReplaceIn), usages, this);
if (myMethodToSearchFor != myMethodToReplaceIn) {
changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethodToSearchFor), usages);
IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myMethodToSearchFor), usages, this);
}
ChangeContextUtil.clearContextInfo(myParameterInitializer);
@@ -469,18 +434,6 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
return (PsiMethod)methodToReplaceIn.getContainingClass().addBefore(delegate, methodToReplaceIn);
}
private void addDefaultConstructor(UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processAddDefaultConstructor(this, usage, usages)) break;
}
}
private void addSuperCall(UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processAddSuperCall(this, usage, usages)) break;
}
}
static PsiType getInitializerType(PsiType forcedType, PsiExpression parameterInitializer, PsiLocalVariable localVariable) {
final PsiType initializerType;
if (forcedType == null) {
@@ -542,22 +495,10 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
});
}
private void changeExternalUsage(UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor: IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processChangeMethodUsage(this, usage, usages)) break;
}
}
protected String getCommandName() {
return RefactoringBundle.message("introduce.parameter.command", UsageViewUtil.getDescriptiveName(myMethodToReplaceIn));
}
private void changeMethodSignatureAndResolveFieldConflicts(UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processChangeMethodSignature(this, usage, usages)) break;
}
}
@Nullable
private static PsiParameter getAnchorParameter(PsiMethod methodToReplaceIn) {
PsiParameterList parameterList = methodToReplaceIn.getParameterList();
@@ -584,20 +525,8 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
return myMethodToSearchFor;
}
public PsiExpression getParameterInitializer() {
return myParameterInitializer;
}
public PsiExpression getExpressionToSearch() {
return myExpressionToSearch;
}
public PsiLocalVariable getLocalVariable() {
return myLocalVariable;
}
public boolean isRemoveLocalVariable() {
return myRemoveLocalVariable;
public JavaExpressionWrapper getParameterInitializer() {
return myInitializerWrapper;
}
@NotNull
@@ -605,10 +534,6 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
return myParameterName;
}
public boolean isReplaceAllOccurences() {
return myReplaceAllOccurences;
}
public boolean isDeclareFinal() {
return myDeclareFinal;
}
@@ -622,10 +547,6 @@ public class IntroduceParameterProcessor extends BaseRefactoringProcessor implem
return myParametersToRemove;
}
public PsiManager getManager() {
return myManager;
}
@NotNull
public Project getProject() {
return myProject;
@@ -0,0 +1,126 @@
/*
* Copyright 2000-2011 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.introduceParameter;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Maxim.Medvedev
*/
public class IntroduceParameterUtil {
private IntroduceParameterUtil() {
}
public static boolean insideMethodToBeReplaced(PsiElement methodUsage, final PsiMethod methodToReplaceIn) {
PsiElement parent = methodUsage.getParent();
while (parent != null) {
if (parent.equals(methodToReplaceIn)) {
return true;
}
parent = parent.getParent();
}
return false;
}
public static boolean isMethodUsage(UsageInfo usageInfo) {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (processor.isMethodUsage(usageInfo)) return true;
}
return false;
}
public static void addSuperCall(UsageInfo usage, UsageInfo[] usages, final IntroduceParameterData data)
throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processAddSuperCall(data, usage, usages)) break;
}
}
public static void addDefaultConstructor(UsageInfo usage, UsageInfo[] usages, final IntroduceParameterData data)
throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processAddDefaultConstructor(data, usage, usages)) break;
}
}
public static void changeExternalUsage(UsageInfo usage, UsageInfo[] usages, final IntroduceParameterData data)
throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processChangeMethodUsage(data, usage, usages)) break;
}
}
public static void changeMethodSignatureAndResolveFieldConflicts(UsageInfo usage,
UsageInfo[] usages,
final IntroduceParameterData data)
throws IncorrectOperationException {
for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) {
if (!processor.processChangeMethodSignature(data, usage, usages)) break;
}
}
public static void processUsages(UsageInfo[] usages, IntroduceParameterData data) {
PsiManager manager = PsiManager.getInstance(data.getProject());
List<UsageInfo> methodUsages = new ArrayList<UsageInfo>();
for (UsageInfo usage : usages) {
if (usage instanceof InternalUsageInfo) continue;
if (usage instanceof DefaultConstructorImplicitUsageInfo) {
addSuperCall(usage, usages, data);
}
else if (usage instanceof NoConstructorClassUsageInfo) {
addDefaultConstructor(usage, usages, data);
}
else {
PsiElement element = usage.getElement();
if (element instanceof PsiMethod) {
if (!manager.areElementsEquivalent(element, data.getMethodToReplaceIn())) {
methodUsages.add(usage);
}
}
else if (!data.isGenerateDelegate()) {
changeExternalUsage(usage, usages, data);
}
}
}
for (UsageInfo usage : methodUsages) {
changeMethodSignatureAndResolveFieldConflicts(usage, usages, data);
}
}
public static boolean isMethodInUsages(IntroduceParameterData data, PsiMethod method, UsageInfo[] usages) {
PsiManager manager = PsiManager.getInstance(data.getProject());
for (UsageInfo info : usages) {
if (!(info instanceof DefaultConstructorImplicitUsageInfo) && manager.areElementsEquivalent(info.getElement(), method)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2000-2011 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.introduceParameter;
import com.intellij.openapi.project.Project;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
/**
* @author Maxim.Medvedev
*/
public class JavaExpressionConverter extends ExpressionConverter {
@Override
protected PsiElement convert(PsiElement expression, Project project) {
return JavaPsiFacade.getElementFactory(project).createExpressionFromText(expression.getText(), expression);
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2000-2011 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.introduceParameter;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
/**
* @author Maxim.Medvedev
*/
public class JavaExpressionWrapper implements IntroduceParameterData.ExpressionWrapper {
private final PsiExpression myExpression;
public JavaExpressionWrapper(@NotNull PsiExpression expression) {
myExpression = expression;
}
@NotNull
@Override
public String getText() {
return myExpression.getText();
}
@Override
public PsiType getType() {
return myExpression.getType();
}
@NotNull
@Override
public PsiElement getExpression() {
return myExpression;
}
}
@@ -17,6 +17,7 @@ package com.intellij.refactoring.introduceParameter;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.lang.Language;
import com.intellij.lang.StdLanguages;
import com.intellij.lang.java.JavaLanguage;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
@@ -28,7 +29,6 @@ import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.util.FieldConflictsResolver;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.javadoc.MethodJavaDocHelper;
import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
@@ -81,18 +81,22 @@ public class JavaIntroduceParameterMethodUsagesProcessor implements IntroducePar
//if we insert parameter in method usage which is contained in method in which we insert this parameter too, we must insert parameter name instead of its initializer
PsiMethod method = PsiTreeUtil.getParentOfType(argList, PsiMethod.class);
if (method != null && isMethodInUsages(data, method, usages)) {
if (method != null && IntroduceParameterUtil.isMethodInUsages(data, method, usages)) {
argList
.addAfter(JavaPsiFacade.getElementFactory(data.getProject()).createExpressionFromText(data.getParameterName(), argList), anchor);
}
else {
ChangeContextUtil.encodeContextInfo(data.getParameterInitializer(), true);
PsiExpression newArg = (PsiExpression)argList.addAfter(data.getParameterInitializer(), anchor);
PsiElement initializer =
ExpressionConverter.getExpression(data.getParameterInitializer().getExpression(), StdLanguages.JAVA, data.getProject());
assert initializer instanceof PsiExpression;
ChangeContextUtil.encodeContextInfo(initializer, true);
PsiExpression newArg = (PsiExpression)argList.addAfter(initializer, anchor);
ChangeContextUtil.decodeContextInfo(newArg, null, null);
ChangeContextUtil.clearContextInfo(data.getParameterInitializer());
ChangeContextUtil.clearContextInfo(initializer);
// here comes some postprocessing...
new OldReferenceResolver(callExpression, newArg, data.getMethodToReplaceIn(), data.getReplaceFieldsWithGetters(),
data.getParameterInitializer()).resolve();
new OldReferenceResolver(callExpression, newArg, data.getMethodToReplaceIn(), data.getReplaceFieldsWithGetters(), initializer)
.resolve();
}
@@ -102,16 +106,6 @@ public class JavaIntroduceParameterMethodUsagesProcessor implements IntroducePar
return false;
}
private static boolean isMethodInUsages(IntroduceParameterData data, PsiMethod method, UsageInfo[] usages) {
PsiManager manager = PsiManager.getInstance(data.getProject());
for (UsageInfo info : usages) {
if (!(info instanceof DefaultConstructorImplicitUsageInfo) && manager.areElementsEquivalent(info.getElement(), method)) {
return true;
}
}
return false;
}
private static void removeParametersFromCall(@NotNull final PsiExpressionList argList, TIntArrayList parametersToRemove) {
final PsiExpression[] exprs = argList.getExpressions();
parametersToRemove.forEachDescending(new TIntProcedure() {
@@ -35,7 +35,7 @@ import java.util.Set;
* Date: Apr 29, 2009 2:03:38 PM
*/
public class OldReferenceResolver {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceParameter.OldRefernceResolver");
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceParameter.OldReferenceResolver");
private final PsiCall myContext;
private final PsiExpression myExpr;
@@ -46,13 +46,13 @@ public class OldReferenceResolver {
private final Project myProject;
private final PsiManager myManager;
private final int myReplaceFieldsWithGetters;
private final PsiExpression myParameterInitializer;
private final PsiElement myParameterInitializer;
public OldReferenceResolver(PsiCall context,
PsiExpression expr,
PsiMethod methodToReplaceIn,
int replaceFieldsWithGetters,
PsiExpression parameterInitializer) throws IncorrectOperationException {
PsiElement parameterInitializer) throws IncorrectOperationException {
myContext = context;
myExpr = expr;
myReplaceFieldsWithGetters = replaceFieldsWithGetters;