diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/ChangedMethodCallInfo.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/ChangedMethodCallInfo.java index 476eca66d02f..d177f306c0d6 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/ChangedMethodCallInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/ChangedMethodCallInfo.java @@ -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); } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/ExpressionConverter.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/ExpressionConverter.java new file mode 100644 index 000000000000..1b0cb1ed51d6 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/ExpressionConverter.java @@ -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 EP = + new LanguageExtension("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); + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/InternalUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/InternalUsageInfo.java index 2faeca05e7b9..1c9d472980c2 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/InternalUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/InternalUsageInfo.java @@ -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); } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterData.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterData.java index 38e8c5af5cdf..e18e1b7c1a13 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterData.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterData.java @@ -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 { + @NotNull + String getText(); + + @Nullable + PsiType getType(); + + @NotNull + RealExpression getExpression(); + + } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterProcessor.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterProcessor.java index f3ceddedc07c..607c3dc0e26d 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterProcessor.java @@ -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 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; diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterUtil.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterUtil.java new file mode 100644 index 000000000000..cb217da86cb2 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterUtil.java @@ -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 methodUsages = new ArrayList(); + + 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; + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaExpressionConverter.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaExpressionConverter.java new file mode 100644 index 000000000000..b384247314ef --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaExpressionConverter.java @@ -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); + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaExpressionWrapper.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaExpressionWrapper.java new file mode 100644 index 000000000000..c4057d168a3a --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaExpressionWrapper.java @@ -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; + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java index c00c2cce8dbd..99c342f137b1 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/JavaIntroduceParameterMethodUsagesProcessor.java @@ -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() { diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java index 1ca50aad8094..c86e04e8eef8 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/OldReferenceResolver.java @@ -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; diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index b7c8d6b25acc..7aa9091780d3 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -105,6 +105,8 @@ + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringBundle.properties index 5e3e6a7a15af..c6b449c5d7a9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringBundle.properties @@ -154,3 +154,5 @@ Field.cannot.be.final.because.replaced.variable.has.lhs.usages=Selected variable field.cannot.be.initialized.in.field.declaration=Field cannot be initialized in declaration field.cannot.be.initialized.in.constructor(s)=Field cannot be initialized in constructor(s) selected.variable.is.used.for.write=Selected variable is used for write +there.is.no.method=There is no containing method +remove.parameter.0.no.longer.used=Remove parameter ''{0}'' no longer used diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java index 760d2dd81d2e..f1c62df9a240 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringSupportProvider.java @@ -31,6 +31,7 @@ import org.jetbrains.plugins.groovy.refactoring.changeSignature.GrChangeSignatur import org.jetbrains.plugins.groovy.refactoring.extractMethod.GroovyExtractMethodHandler; import org.jetbrains.plugins.groovy.refactoring.introduce.constant.GrIntroduceConstantHandler; import org.jetbrains.plugins.groovy.refactoring.introduce.field.GrIntroduceFieldHandler; +import org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterHandler; import org.jetbrains.plugins.groovy.refactoring.introduce.variable.GroovyIntroduceVariableHandler; /** @@ -80,6 +81,11 @@ public class GroovyRefactoringSupportProvider extends RefactoringSupportProvider return new GrIntroduceFieldHandler(); } + @Override + public RefactoringActionHandler getIntroduceParameterHandler() { + return new GrIntroduceParameterHandler(); + } + @Override public RefactoringActionHandler getIntroduceConstantHandler() { return new GrIntroduceConstantHandler(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringUtil.java index 71c009eb3c6e..8d22f5555172 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/GroovyRefactoringUtil.java @@ -59,6 +59,7 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstant; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.util.GrDeclarationHolder; +import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner; import java.util.*; @@ -469,7 +470,7 @@ public abstract class GroovyRefactoringUtil { /* if (declareFinal) { com.intellij.psi.util.PsiUtil.setModifierProperty((decl.getMembers()[0]), PsiModifier.FINAL, true); }*/ - ((GrCodeBlock)anchorStatement.getParent()).addStatementBefore(decl, anchorStatement); + ((GrStatementOwner)anchorStatement.getParent()).addStatementBefore(decl, anchorStatement); return id; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/FieldConflictsResolver.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/FieldConflictsResolver.java new file mode 100644 index 000000000000..710c1b01e597 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/FieldConflictsResolver.java @@ -0,0 +1,148 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrThisReferenceExpression; +import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Maxim.Medvedev + */ +public class FieldConflictsResolver { + private static final Logger LOG = + Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.introduce.parameter.FieldConflictsResolver"); + private final GrCodeBlock myScope; + private PsiField myField = null; + private List myReferenceExpressions = null; + private PsiClass myQualifyingClass; + + public FieldConflictsResolver(String name, GrCodeBlock scope) { + myScope = scope; + if (myScope == null) return; + + final GroovyPsiElement resolved = ResolveUtil.resolveProperty(myScope, name); + if (resolved instanceof GrReferenceExpression || resolved == null) return; + + assert resolved instanceof PsiVariable; + final PsiVariable oldVariable = (PsiVariable)resolved; + myField = oldVariable instanceof PsiField ? (PsiField) oldVariable : null; + if (!(oldVariable instanceof PsiField)) return; + + myReferenceExpressions = new ArrayList(); + for (PsiReference reference : ReferencesSearch.search(myField, new LocalSearchScope(myScope), false)) { + final PsiElement element = reference.getElement(); + if (element instanceof GrReferenceExpression) { + final GrReferenceExpression referenceExpression = (GrReferenceExpression)element; + if (referenceExpression.getQualifier() == null) { + myReferenceExpressions.add(referenceExpression); + } + } + } + if (myField.hasModifierProperty(PsiModifier.STATIC)) { + myQualifyingClass = myField.getContainingClass(); + } + } + + public GrExpression fixInitializer(GrExpression initializer) { + if (myField == null) return initializer; + final GrReferenceExpression[] replacedRef = {null}; + initializer.accept(new GroovyRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(GrReferenceExpression expression) { + final GrExpression qualifierExpression = expression.getQualifier(); + if (qualifierExpression != null) { + qualifierExpression.accept(this); + } + else { + final PsiElement result = expression.resolve(); + if (expression.getManager().areElementsEquivalent(result, myField)) { + try { + replacedRef[0] = qualifyReference(expression, myField, myQualifyingClass); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + } + } + }); + if (!initializer.isValid()) return replacedRef[0]; + return initializer; + } + + public void fix() throws IncorrectOperationException { + if (myField == null) return; + final PsiManager manager = myScope.getManager(); + for (GrReferenceExpression referenceExpression : myReferenceExpressions) { + if (!referenceExpression.isValid()) continue; + final PsiElement newlyResolved = referenceExpression.resolve(); + if (!manager.areElementsEquivalent(newlyResolved, myField)) { + qualifyReference(referenceExpression, myField, myQualifyingClass); + } + } + } + + + public static GrReferenceExpression qualifyReference(GrReferenceExpression referenceExpression, + final PsiMember member, + @Nullable final PsiClass qualifyingClass) throws IncorrectOperationException { + PsiManager manager = referenceExpression.getManager(); + GrReferenceExpression expressionFromText; + final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(referenceExpression.getProject()); + if (qualifyingClass == null) { + PsiClass parentClass = PsiTreeUtil.getParentOfType(referenceExpression, PsiClass.class); + final PsiClass containingClass = member.getContainingClass(); + if (parentClass != null && !InheritanceUtil.isInheritorOrSelf(parentClass, containingClass, true)) { + while (parentClass != null && !InheritanceUtil.isInheritorOrSelf(parentClass, containingClass, true)) { + parentClass = PsiTreeUtil.getParentOfType(parentClass, PsiClass.class, true); + } + LOG.assertTrue(parentClass != null); + expressionFromText = factory.createReferenceExpressionFromText("A.this." + member.getName()); + //noinspection ConstantConditions + ((GrThisReferenceExpression)expressionFromText.getQualifier()).getQualifier().replace( + factory.createReferenceElementForClass(parentClass)); + } + else { + expressionFromText = (GrReferenceExpression)factory.createExpressionFromText("this." + member.getName()); + } + } + else { + expressionFromText = (GrReferenceExpression)factory.createExpressionFromText("A." + member.getName()); + expressionFromText.setQualifier(factory.createReferenceElementForClass(qualifyingClass)); + } + CodeStyleManager codeStyleManager = manager.getCodeStyleManager(); + expressionFromText = (GrReferenceExpression)codeStyleManager.reformat(expressionFromText); + return (GrReferenceExpression)referenceExpression.replace(expressionFromText); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrExpressionWrapper.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrExpressionWrapper.java new file mode 100644 index 000000000000..800d3e45e06c --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrExpressionWrapper.java @@ -0,0 +1,50 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiType; +import com.intellij.refactoring.introduceParameter.IntroduceParameterData; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; + +/** + * @author Maxim.Medvedev + */ +public class GrExpressionWrapper implements IntroduceParameterData.ExpressionWrapper { + private final GrExpression myExpression; + + public GrExpressionWrapper(GrExpression 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; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterContext.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterContext.java new file mode 100644 index 000000000000..725b07fd86cc --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterContext.java @@ -0,0 +1,34 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.psi.PsiMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext; + +/** + * @author Maxim.Medvedev + */ +public class GrIntroduceParameterContext extends GrIntroduceContext { + public final PsiMethod methodToSearchFor; + public final GrMethod methodToReplaceIn; + + public GrIntroduceParameterContext(GrIntroduceContext context, GrMethod methodToReplaceIn, PsiMethod methodToSearchFor) { + super(context.project, context.editor, context.expression, context.occurrences, context.scope, context.var); + this.methodToReplaceIn = methodToReplaceIn; + this.methodToSearchFor = methodToSearchFor; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterDialog.form b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterDialog.form new file mode 100644 index 000000000000..47a0b79b89d2 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterDialog.form @@ -0,0 +1,144 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterDialog.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterDialog.java new file mode 100644 index 000000000000..cdb324a04bdc --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterDialog.java @@ -0,0 +1,252 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiType; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.refactoring.JavaRefactoringSettings; +import com.intellij.refactoring.ui.NameSuggestionsField; +import com.intellij.refactoring.ui.RefactoringDialog; +import com.intellij.util.ui.GridBag; +import gnu.trove.TIntArrayList; +import gnu.trove.TObjectIntHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.GroovyFileType; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.refactoring.GroovyNameSuggestionUtil; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceDialog; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceRefactoringError; +import org.jetbrains.plugins.groovy.refactoring.introduce.field.GroovyFieldValidator; +import org.jetbrains.plugins.groovy.refactoring.ui.GrTypeComboBox; + +import javax.swing.*; +import java.awt.*; + +import static com.intellij.refactoring.IntroduceParameterRefactoring.*; + +public class GrIntroduceParameterDialog extends RefactoringDialog implements GrIntroduceDialog { + private JPanel myContentPane; + private GrTypeComboBox myTypeComboBox; + private NameSuggestionsField myNameSuggestionsField; + private JCheckBox myDeclareFinalCheckBox; + private JCheckBox myDelegateViaOverloadingMethodCheckBox; + private JPanel myCheckBoxContainer; + private JCheckBox myReplaceAllOccurrencesCheckBox; + private JRadioButton myDoNotReplaceRadioButton; + private JRadioButton myReplaceFieldsInaccessibleInRadioButton; + private JRadioButton myReplaceAllFieldsRadioButton; + private JCheckBox myRemoveLocalVariableCheckBox; + private JPanel myGetterPanel; + private GrIntroduceParameterContext myContext; + TObjectIntHashMap toRemoveCBs; + + public GrIntroduceParameterDialog(GrIntroduceParameterContext context, TObjectIntHashMap parametersToRemove) { + super(context.project, true); + myContext = context; + toRemoveCBs = new TObjectIntHashMap(parametersToRemove.size()); + for (Object p : parametersToRemove.keys()) { + JCheckBox cb = new JCheckBox(GroovyRefactoringBundle.message("remove.parameter.0.no.longer.used", ((GrParameter)p).getName())); + toRemoveCBs.put(cb, parametersToRemove.get((GrParameter)p)); + cb.setSelected(true); + } + + JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance(); + + final PsiElement[] occurrences = context.occurrences; + if (occurrences.length < 2) { + myReplaceAllOccurrencesCheckBox.setSelected(true); + myReplaceAllOccurrencesCheckBox.setVisible(false); + } + if (myContext.var == null) { + myRemoveLocalVariableCheckBox.setSelected(false); + myRemoveLocalVariableCheckBox.setVisible(false); + } + else { + myRemoveLocalVariableCheckBox.setSelected(settings.INTRODUCE_PARAMETER_DELETE_LOCAL_VARIABLE); + } + + initReplaceFieldsWithGetters(settings); + + myDeclareFinalCheckBox.setSelected(hasFinalModifier()); + init(); + } + + private void initReplaceFieldsWithGetters(JavaRefactoringSettings settings) { + final GrExpression expression = myContext.expression; + GroovyIntroduceParameterUtil.findUsedFieldsWithGetters(expression, myContext.methodToReplaceIn.getContainingClass()); + switch (settings.INTRODUCE_PARAMETER_REPLACE_FIELDS_WITH_GETTERS) { + case REPLACE_FIELDS_WITH_GETTERS_ALL: + myReplaceAllFieldsRadioButton.setSelected(true); + break; + case REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE: + myReplaceFieldsInaccessibleInRadioButton.setSelected(true); + break; + case REPLACE_FIELDS_WITH_GETTERS_NONE: + myDoNotReplaceRadioButton.setSelected(true); + break; + } + } + + private boolean hasFinalModifier() { + final Boolean createFinals = JavaRefactoringSettings.getInstance().INTRODUCE_PARAMETER_CREATE_FINALS; + return createFinals == null ? CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS : createFinals.booleanValue(); + } + + @Override + protected void doAction() { + saveSettings(); + GrIntroduceParameterSettings settings = new GrIntroduceParameterSettingsImpl( + myNameSuggestionsField.getEnteredName(), + myReplaceAllOccurrencesCheckBox.isSelected(), + myTypeComboBox.getSelectedType(), + myDeclareFinalCheckBox.isSelected(), + myDelegateViaOverloadingMethodCheckBox.isSelected(), + getParametersToRemove(), + getReplaceFieldsWithGetter(), + myRemoveLocalVariableCheckBox.isSelected()); + invokeRefactoring(new GrIntroduceParameterProcessor(settings, myContext)); + } + + private void saveSettings() { + final JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance(); + settings.INTRODUCE_PARAMETER_CREATE_FINALS = myDeclareFinalCheckBox.isSelected(); + if (myRemoveLocalVariableCheckBox.isVisible()) { + settings.INTRODUCE_PARAMETER_DELETE_LOCAL_VARIABLE = myRemoveLocalVariableCheckBox.isSelected(); + } + if (myGetterPanel.isVisible()) { + settings.INTRODUCE_PARAMETER_REPLACE_FIELDS_WITH_GETTERS = getReplaceFieldsWithGetter(); + } + } + + @Override + protected JComponent createCenterPanel() { + final GridBag c = new GridBag().setDefaultAnchor(GridBagConstraints.WEST).setLine(3); + for (Object o : toRemoveCBs.keys()) { + c.nextLine(); + myCheckBoxContainer.add(((JCheckBox)o), c); + } + return myContentPane; + } + + @Override + public GrIntroduceParameterSettings getSettings() { + return null; + } + + private int getReplaceFieldsWithGetter() { + if (myDoNotReplaceRadioButton.isSelected()) return REPLACE_FIELDS_WITH_GETTERS_NONE; + if (myReplaceFieldsInaccessibleInRadioButton.isSelected()) return REPLACE_FIELDS_WITH_GETTERS_INACCESSIBLE; + if (myReplaceAllFieldsRadioButton.isSelected()) return REPLACE_FIELDS_WITH_GETTERS_ALL; + throw new GrIntroduceRefactoringError("no check box selected"); + } + + private TIntArrayList getParametersToRemove() { + TIntArrayList list = new TIntArrayList(); + for (Object o : toRemoveCBs.keys()) { + if (((JCheckBox)o).isSelected()) { + list.add(toRemoveCBs.get((JCheckBox)o)); + } + } + return list; + } + + private void createUIComponents() { + myTypeComboBox = new GrTypeComboBox(myContext.var != null ? myContext.var.getDeclaredType() : myContext.expression.getType()); + + String[] possibleNames = GroovyNameSuggestionUtil.suggestVariableNames(myContext.expression, new GroovyFieldValidator(myContext), true); + if (myContext.var != null) { + String[] arr = new String[possibleNames.length + 1]; + arr[0] = myContext.var.getName(); + System.arraycopy(possibleNames, 0, arr, 1, possibleNames.length); + possibleNames = arr; + } + myNameSuggestionsField = new NameSuggestionsField(possibleNames, myContext.project, GroovyFileType.GROOVY_FILE_TYPE); + } + + static class GrIntroduceParameterSettingsImpl implements GrIntroduceParameterSettings { + + private String myName; + private boolean myReplaceAllOccurrences; + private PsiType mySelectedType; + private boolean myDeclareFinal; + private boolean myIsGenerateDelegate; + private TIntArrayList myParameterToRemove; + private int myReplaceFieldWithGetters; + private boolean myRemoveLocalVariable; + + GrIntroduceParameterSettingsImpl(String name, + boolean replaceAllOccurrences, + PsiType selectedType, + boolean declareFinal, + boolean isGenerateDelegate, + TIntArrayList parameterToRemove, + int replaceFieldWithGetters, + boolean removeLocalVariable) { + myName = name; + myReplaceAllOccurrences = replaceAllOccurrences; + mySelectedType = selectedType; + myDeclareFinal = declareFinal; + myIsGenerateDelegate = isGenerateDelegate; + myParameterToRemove = parameterToRemove; + myReplaceFieldWithGetters = replaceFieldWithGetters; + myRemoveLocalVariable = removeLocalVariable; + } + + @Override + public String getName() { + return myName; + } + + @Override + public boolean replaceAllOccurrences() { + return myReplaceAllOccurrences; + } + + @Override + public PsiType getSelectedType() { + return mySelectedType; + } + + @Override + public boolean declareFinal() { + return myDeclareFinal; + } + + @Override + public boolean removeLocalVariable() { + return myRemoveLocalVariable; + } + + @Override + public boolean generateDelegate() { + return myIsGenerateDelegate; + } + + @NotNull + @Override + public TIntArrayList parametersToRemove() { + return myParameterToRemove; + } + + @Override + public int replaceFieldsWithGetters() { + return myReplaceFieldWithGetters; + } + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java new file mode 100644 index 000000000000..4563acfcbe4a --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterHandler.java @@ -0,0 +1,103 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler; +import gnu.trove.TObjectIntHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceDialog; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceRefactoringError; + +/** + * @author Maxim.Medvedev + */ +public class GrIntroduceParameterHandler extends GrIntroduceHandlerBase { + + @Override + protected String getRefactoringName() { + return RefactoringBundle.message("introduce.parameter.title"); + } + + @Override + protected String getHelpID() { + return HelpID.INTRODUCE_PARAMETER; + } + + @NotNull + @Override + protected PsiElement findScope(GrExpression expression, GrVariable variable) { + @NotNull PsiElement place = expression == null ? variable : expression; + + final PsiMethod method = PsiTreeUtil.getParentOfType(place, PsiMethod.class, true, PsiClass.class); + if (method == null) throw new GrIntroduceRefactoringError(GroovyRefactoringBundle.message("there.is.no.method")); + return method; + } + + @Override + public GrIntroduceContext getContext(Project project, Editor editor, GrExpression expression, @Nullable GrVariable variable) { + final GrIntroduceContext context = super.getContext(project, editor, expression, variable); + + assert context.scope instanceof GrMethod; + GrMethod curMethod = (GrMethod)context.scope; + + final PsiMethod methodToSearchFor = IntroduceParameterHandler.chooseEnclosingMethod(curMethod); + + return new GrIntroduceParameterContext(context, curMethod, methodToSearchFor); + } + + @Override + protected void checkExpression(GrExpression selectedExpr) throws GrIntroduceRefactoringError { + //nothing to do + } + + @Override + protected void checkVariable(GrVariable variable) throws GrIntroduceRefactoringError { + //nothing to do + } + + @Override + protected void checkOccurrences(PsiElement[] occurrences) { + //nothing to do + } + + @Override + protected GrIntroduceDialog getDialog(GrIntroduceContext context) { + assert context instanceof GrIntroduceParameterContext; + TObjectIntHashMap toRemove = GroovyIntroduceParameterUtil.findParametersToRemove(context); + return new GrIntroduceParameterDialog((GrIntroduceParameterContext)context, toRemove); + } + + @Override + public GrVariable runRefactoring(GrIntroduceContext context, GrIntroduceParameterSettings settings) { + return null; + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java new file mode 100644 index 000000000000..6b0ed1822608 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterProcessor.java @@ -0,0 +1,472 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.search.searches.MethodReferencesSearch; +import com.intellij.psi.search.searches.OverridingMethodsSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.IntroduceParameterRefactoring; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.introduceParameter.*; +import com.intellij.refactoring.ui.UsageViewDescriptorAdapter; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.refactoring.util.ConflictsUtil; +import com.intellij.refactoring.util.RefactoringUIUtil; +import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo; +import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo; +import com.intellij.usageView.UsageInfo; +import com.intellij.usageView.UsageViewDescriptor; +import com.intellij.usageView.UsageViewUtil; +import com.intellij.util.containers.MultiMap; +import gnu.trove.TIntArrayList; +import gnu.trove.TIntProcedure; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrSuperReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; +import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author Maxim.Medvedev + */ +public class GrIntroduceParameterProcessor extends BaseRefactoringProcessor implements IntroduceParameterData { + private static final Logger LOG = + Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.introduce.parameter.GrIntroduceParameterProcessor"); + + private final GrIntroduceParameterSettings mySettings; + private final GrIntroduceParameterContext myContext; + private IntroduceParameterData.ExpressionWrapper myParameterInitializer; + + public GrIntroduceParameterProcessor(GrIntroduceParameterSettings settings, GrIntroduceParameterContext context) { + super(context.project); + this.mySettings = settings; + this.myContext = context; + + myParameterInitializer = new GrExpressionWrapper(this.myContext.expression); + } + + @Override + protected UsageViewDescriptor createUsageViewDescriptor(final UsageInfo[] usages) { + return new UsageViewDescriptorAdapter() { + @NotNull + @Override + public PsiElement[] getElements() { + return new PsiElement[]{myContext.methodToSearchFor}; + } + + @Override + public String getProcessedElementsHeader() { + return RefactoringBundle.message("introduce.parameter.elements.header"); + } + }; + } + + @Override + protected boolean preprocessUsages(Ref refUsages) { + UsageInfo[] usagesIn = refUsages.get(); + MultiMap conflicts = new MultiMap(); + + /*AnySameNameVariables anySameNameVariables = new AnySameNameVariables(); + myMethodToReplaceIn.accept(anySameNameVariables); + final Pair conflictPair = anySameNameVariables.getConflict(); + if (conflictPair != null) { + conflicts.putValue(conflictPair.first, conflictPair.second); + }*/ + + if (!mySettings.generateDelegate()) { + detectAccessibilityConflicts(usagesIn, conflicts); + } + + if (myContext.expression != null && !myContext.methodToReplaceIn.hasModifierProperty(PsiModifier.PRIVATE)) { + final AnySupers anySupers = new AnySupers(); + myContext.expression.accept(anySupers); + if (anySupers.isResult()) { + for (UsageInfo usageInfo : usagesIn) { + if (!(usageInfo.getElement() instanceof PsiMethod) && !(usageInfo instanceof InternalUsageInfo)) { + if (!PsiTreeUtil.isAncestor(myContext.methodToReplaceIn.getContainingClass(), usageInfo.getElement(), false)) { + conflicts.putValue(myContext.expression, + RefactoringBundle.message("parameter.initializer.contains.0.but.not.all.calls.to.method.are.in.its.class", + CommonRefactoringUtil.htmlEmphasize(PsiKeyword.SUPER))); + break; + } + } + } + } + } + + for (IntroduceParameterMethodUsagesProcessor processor : IntroduceParameterMethodUsagesProcessor.EP_NAME.getExtensions()) { + processor.findConflicts(this, refUsages.get(), conflicts); + } + + return showConflicts(conflicts, usagesIn); + } + + private void detectAccessibilityConflicts(final UsageInfo[] usageArray, MultiMap conflicts) { + if (myContext.expression == null) return; + + final ReferencedElementsCollector collector = new ReferencedElementsCollector(); + myContext.expression.accept(collector); + final List result = collector.getResult(); + if (result.isEmpty()) return; + + for (final UsageInfo usageInfo : usageArray) { + if (!(usageInfo instanceof ExternalUsageInfo) || !IntroduceParameterUtil.isMethodUsage(usageInfo)) continue; + + final PsiElement place = usageInfo.getElement(); + for (PsiElement element : result) { + if (element instanceof PsiField && + mySettings.replaceFieldsWithGetters() != IntroduceParameterRefactoring.REPLACE_FIELDS_WITH_GETTERS_NONE) { + //check getter access instead + final PsiClass psiClass = ((PsiField)element).getContainingClass(); + LOG.assertTrue(psiClass != null); + final PsiMethod method = GroovyPropertyUtils.findGetterForField((PsiField)element); + if (method != null) { + element = method; + } + } + if (element instanceof PsiMember && + !JavaPsiFacade.getInstance(myProject).getResolveHelper().isAccessible((PsiMember)element, place, null)) { + String message = RefactoringBundle.message( + "0.is.not.accessible.from.1.value.for.introduced.parameter.in.that.method.call.will.be.incorrect", + RefactoringUIUtil.getDescription(element, true), + RefactoringUIUtil.getDescription(ConflictsUtil.getContainer(place), true)); + conflicts.putValue(element, message); + } + } + } + } + + private static class AnySupers extends GroovyRecursiveElementVisitor { + boolean myContainsSupers = false; + + @Override + public void visitSuperExpression(GrSuperReferenceExpression superExpression) { + super.visitSuperExpression(superExpression); + myContainsSupers = true; + } + + boolean isResult() { + return myContainsSupers; + } + } + + private static class ReferencedElementsCollector extends GroovyRecursiveElementVisitor { + + private List myResult = new ArrayList(); + + @Override + public void visitReferenceExpression(GrReferenceExpression referenceExpression) { + add(referenceExpression); + } + + private void add(GrReferenceElement referenceExpression) { + final PsiElement resolved = referenceExpression.resolve(); + if (resolved != null) { + myResult.add(resolved); + } + } + + @Override + public void visitCodeReferenceElement(GrCodeReferenceElement refElement) { + add(refElement); + } + + public List getResult() { + return myResult; + } + } + + @NotNull + @Override + protected UsageInfo[] findUsages() { + ArrayList result = new ArrayList(); + + final PsiMethod methodToSearchFor = myContext.methodToSearchFor; + + if (!mySettings.generateDelegate()) { + Collection refs = + MethodReferencesSearch.search(methodToSearchFor, GlobalSearchScope.projectScope(myProject), true).findAll(); + + for (PsiReference ref1 : refs) { + PsiElement ref = ref1.getElement(); + if (ref instanceof PsiMethod && ((PsiMethod)ref).isConstructor()) { + DefaultConstructorImplicitUsageInfo implicitUsageInfo = + new DefaultConstructorImplicitUsageInfo((PsiMethod)ref, ((PsiMethod)ref).getContainingClass(), methodToSearchFor); + result.add(implicitUsageInfo); + } + else if (ref instanceof PsiClass) { + result.add(new NoConstructorClassUsageInfo((PsiClass)ref)); + } + else if (!IntroduceParameterUtil.insideMethodToBeReplaced(ref, myContext.methodToReplaceIn)) { + result.add(new ExternalUsageInfo(ref)); + } + else { + result.add(new ChangedMethodCallInfo(ref)); + } + } + } + + if (mySettings.replaceAllOccurrences()) { + PsiElement[] exprs = myContext.occurrences; + for (PsiElement expr : exprs) { + result.add(new InternalUsageInfo(expr)); + } + } + else { + if (myContext.expression != null) { + result.add(new InternalUsageInfo(myContext.expression)); + } + } + + Collection overridingMethods = + OverridingMethodsSearch.search(methodToSearchFor, methodToSearchFor.getUseScope(), true).findAll(); + + for (PsiMethod overridingMethod : overridingMethods) { + result.add(new UsageInfo(overridingMethod)); + } + + final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]); + return UsageViewUtil.removeDuplicatedUsages(usageInfos); + } + + @Override + protected void performRefactoring(UsageInfo[] usages) { + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); + + PsiType initializerType = mySettings.getSelectedType(); + + // Changing external occurences (the tricky part) + + IntroduceParameterUtil.processUsages(usages, this); + + final boolean methodsToProcessAreDifferent = myContext.methodToReplaceIn != myContext.methodToSearchFor; + if (mySettings.generateDelegate()) { + generateDelegate(myContext.methodToReplaceIn); + if (methodsToProcessAreDifferent) { + final GrMethod method = generateDelegate(myContext.methodToSearchFor); + final PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && containingClass.isInterface()) { + final GrOpenBlock block = method.getBlock(); + if (block != null) { + block.delete(); + } + } + } + } + + // Changing signature of initial method + // (signature of myMethodToReplaceIn will be either changed now or have already been changed) + LOG.assertTrue(initializerType == null || initializerType.isValid()); + + final FieldConflictsResolver fieldConflictsResolver = + new FieldConflictsResolver(mySettings.getName(), myContext.methodToReplaceIn.getBlock()); + IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myContext.methodToReplaceIn), usages, this); + if (methodsToProcessAreDifferent) { + IntroduceParameterUtil.changeMethodSignatureAndResolveFieldConflicts(new UsageInfo(myContext.methodToSearchFor), usages, this); + } + + // Replacing expression occurences + for (UsageInfo usage : usages) { + if (usage instanceof ChangedMethodCallInfo) { + PsiElement element = usage.getElement(); + + processChangedMethodCall(element); + } + else if (usage instanceof InternalUsageInfo) { + PsiElement element = usage.getElement(); + if (element instanceof GrExpression) { + element = PsiUtil.skipParentheses(element, true); + } + + GrExpression newExpr = factory.createExpressionFromText(mySettings.getName()); + element.replace(newExpr); + } + } + + if (myContext.var != null && mySettings.removeLocalVariable()) { + myContext.var.delete(); + } + fieldConflictsResolver.fix(); + } + + private GrMethod generateDelegate(PsiMethod prototype) { + final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); + + GrMethod result; + if (prototype instanceof GrMethod) { + result = (GrMethod)prototype.copy(); + } + else { + StringBuilder builder = new StringBuilder(); + builder.append(prototype.getModifierList().getText()).append(' '); + + if (prototype.getReturnTypeElement() != null) { + builder.append(prototype.getReturnTypeElement().getText()); + } + builder.append(' ').append(prototype.getName()); + builder.append(prototype.getParameterList().getText()); + builder.append("{}"); + result = factory.createMethodFromText(builder.toString()); + } + + StringBuilder call = new StringBuilder(); + call.append("def foo(){\n").append(prototype.getName()).append('('); + final GrParameter[] parameters = result.getParameters(); + for (GrParameter parameter : parameters) { + call.append(parameter.getName()).append(", "); + } + call.delete(call.length() - 2, call.length()); + call.append(myParameterInitializer.getText()); + call.append(");\n}"); + final GrOpenBlock block = factory.createMethodFromText(call.toString()).getBlock(); + + result.getBlock().replace(block); + final PsiElement parent = myContext.methodToReplaceIn.getParent(); + return (GrMethod)parent.addBefore(result, myContext.methodToReplaceIn); + } + + private void processChangedMethodCall(PsiElement element) { + if (element.getParent() instanceof GrMethodCallExpression) { + GrMethodCallExpression methodCall = (GrMethodCallExpression)element.getParent(); + + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); + GrExpression expression = factory.createExpressionFromText(mySettings.getName(), null); + final GrArgumentList argList = methodCall.getArgumentList(); + final PsiElement[] exprs = argList.getAllArguments(); + + if (exprs.length > 0) { + argList.addAfter(expression, exprs[exprs.length - 1]); + } + else { + argList.add(expression); + } + + removeParametersFromCall(methodCall, argList); + + } + else { + LOG.error(element.getParent()); + } + + } + + private void removeParametersFromCall(GrMethodCallExpression methodCall, GrArgumentList argList) { + final GroovyResolveResult resolveResult = methodCall.advancedResolve(); + final PsiElement resolved = resolveResult.getElement(); + LOG.assertTrue(resolved instanceof PsiMethod); + final GrClosureSignature signature = GrClosureSignatureUtil.createSignature((PsiMethod)resolved, resolveResult.getSubstitutor()); + final GrClosureSignatureUtil.ArgInfo[] argInfos = + GrClosureSignatureUtil.mapParametersToArguments(signature, argList, methodCall, methodCall.getClosureArguments()); + LOG.assertTrue(argInfos != null); + mySettings.parametersToRemove().forEach(new TIntProcedure() { + @Override + public boolean execute(int value) { + final List args = argInfos[value].args; + for (PsiElement arg : args) { + arg.delete(); + } + return true; + } + }); + } + + @Override + protected String getCommandName() { + return RefactoringBundle.message("introduce.parameter.command", UsageViewUtil.getDescriptiveName(myContext.methodToReplaceIn)); + } + + @NotNull + @Override + public Project getProject() { + return myContext.project; + } + + @Override + public PsiMethod getMethodToReplaceIn() { + return myContext.methodToReplaceIn; + } + + @NotNull + @Override + public PsiMethod getMethodToSearchFor() { + return myContext.methodToSearchFor; + } + + @Override + public IntroduceParameterData.ExpressionWrapper getParameterInitializer() { + return myParameterInitializer; + } + + @NotNull + @Override + public String getParameterName() { + return mySettings.getName(); + } + + @Override + public int getReplaceFieldsWithGetters() { + return mySettings.replaceFieldsWithGetters(); + } + + @Override + public boolean isDeclareFinal() { + return mySettings.declareFinal(); + } + + @Override + public boolean isGenerateDelegate() { + return mySettings.generateDelegate(); + } + + @NotNull + @Override + public PsiType getForcedType() { + final PsiType selectedType = mySettings.getSelectedType(); + if (selectedType != null) return selectedType; + final PsiManager manager = PsiManager.getInstance(myProject); + final GlobalSearchScope resolveScope = myContext.methodToReplaceIn.getResolveScope(); + return PsiType.getJavaLangObject(manager, resolveScope); + } + + @NotNull + @Override + public TIntArrayList getParametersToRemove() { + return mySettings.parametersToRemove(); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterSettings.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterSettings.java new file mode 100644 index 000000000000..152864587131 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GrIntroduceParameterSettings.java @@ -0,0 +1,36 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import gnu.trove.TIntArrayList; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceSettings; + +/** + * @author Maxim.Medvedev + */ +public interface GrIntroduceParameterSettings extends GrIntroduceSettings { + boolean generateDelegate(); + TIntArrayList parametersToRemove(); + + /** + * @see com.intellij.refactoring.IntroduceParameterRefactoring + */ + int replaceFieldsWithGetters(); + + boolean declareFinal(); + + boolean removeLocalVariable(); +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyExpressionConverter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyExpressionConverter.java new file mode 100644 index 000000000000..0be8e95f4588 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyExpressionConverter.java @@ -0,0 +1,31 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.introduceParameter.ExpressionConverter; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; + +/** + * @author Maxim.Medvedev + */ +public class GroovyExpressionConverter extends ExpressionConverter { + @Override + protected PsiElement convert(PsiElement expression, Project project) { + return GroovyPsiElementFactory.getInstance(project).createExpressionFromText(expression.getText(), expression); + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java new file mode 100644 index 000000000000..e2df2a6244e3 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/GroovyIntroduceParameterUtil.java @@ -0,0 +1,99 @@ +/* + * 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 org.jetbrains.plugins.groovy.refactoring.introduce.parameter; + +import com.intellij.psi.*; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; +import gnu.trove.TObjectIntHashMap; +import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrThisReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; +import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Maxim.Medvedev + */ +public class GroovyIntroduceParameterUtil { + private GroovyIntroduceParameterUtil() { + } + + public static PsiField[] findUsedFieldsWithGetters(GrExpression expression, PsiClass containingClass) { + final FieldSearcher searcher = new FieldSearcher(containingClass); + expression.accept(searcher); + return searcher.getResult(); + } + + public static TObjectIntHashMap findParametersToRemove(GrIntroduceContext context) { + TObjectIntHashMap toRemove = new TObjectIntHashMap(); + if (context.var == null) { + final GrMethod method = (GrMethod)context.scope; + final GrParameter[] parameters = method.getParameters(); + final GrExpression expr = context.expression; + for (int i = 0; i < parameters.length; i++) { + GrParameter parameter = parameters[i]; + final boolean shouldRemove = ReferencesSearch.search(parameter).forEach(new Processor() { + @Override + public boolean process(PsiReference ref) { + final PsiElement element = ref.getElement(); + if (element == null) return false; + return PsiTreeUtil.isAncestor(expr, element, false); + } + }); + if (shouldRemove) { + toRemove.put(parameter, i); + } + } + } + return toRemove; + } + + private static class FieldSearcher extends GroovyRecursiveElementVisitor { + PsiClass myClass; + private final List result = new ArrayList(); + + private FieldSearcher(PsiClass aClass) { + myClass = aClass; + } + + public PsiField[] getResult() { + return ContainerUtil.toArray(result, new PsiField[result.size()]); + } + + @Override + public void visitReferenceExpression(GrReferenceExpression ref) { + super.visitReferenceExpression(ref); + final GrExpression qualifier = ref.getQualifier(); + if (qualifier != null && !(qualifier instanceof GrThisReferenceExpression)) return; + + final PsiElement resolved = ref.resolve(); + if (!(resolved instanceof PsiField)) return; + final PsiMethod getter = GroovyPropertyUtils.findGetterForField((PsiField)resolved); + if (getter != null) { + result.add((PsiField)resolved); + } + } + } +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/GroovyIntroduceParameterMethodUsagesProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/GroovyIntroduceParameterMethodUsagesProcessor.java index 3d3b94bff1ec..ef95bcd68036 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/GroovyIntroduceParameterMethodUsagesProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/GroovyIntroduceParameterMethodUsagesProcessor.java @@ -17,16 +17,16 @@ package org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy; import com.intellij.codeInsight.ChangeContextUtil; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.javadoc.PsiDocTag; +import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.introduceParameter.ExpressionConverter; import com.intellij.refactoring.introduceParameter.IntroduceParameterData; import com.intellij.refactoring.introduceParameter.IntroduceParameterMethodUsagesProcessor; +import com.intellij.refactoring.introduceParameter.IntroduceParameterUtil; import com.intellij.refactoring.util.javadoc.MethodJavaDocHelper; -import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo; import com.intellij.usageView.UsageInfo; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; @@ -37,25 +37,30 @@ import gnu.trove.TIntProcedure; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.GroovyFileType; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifier; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrConstructorInvocation; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; -import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; +import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; -import java.util.HashSet; -import java.util.Set; - /** * @author Maxim.Medvedev * Date: Apr 18, 2009 3:16:24 PM */ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceParameterMethodUsagesProcessor { + private static final Logger LOG = Logger + .getInstance("#org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy.GroovyIntroduceParameterMethodUsagesProcessor"); private static boolean isGroovyUsage(UsageInfo usage) { final PsiElement el = usage.getElement(); @@ -67,33 +72,6 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP } public void findConflicts(IntroduceParameterData data, UsageInfo[] usages, MultiMap conflicts) { - Set groovyUsages = new HashSet(); - for (UsageInfo usage : usages) { - if (isMethodUsage(usage)) groovyUsages.add(usage); - } - if (groovyUsages.size() == 0) return; - data.getParameterInitializer().accept(new InitializerVisitor(conflicts)); - } - - private static class InitializerVisitor extends JavaRecursiveElementWalkingVisitor { - private final MultiMap conflicts; - - private InitializerVisitor(MultiMap conflicts) { - this.conflicts = conflicts; - } - - @Override - public void visitNewExpression(PsiNewExpression expression) { - super.visitNewExpression(expression); - final PsiExpression qualifier = expression.getQualifier(); - if (qualifier != null) { - conflicts.putValue(qualifier, GroovyRefactoringBundle.message("groovy.does.not.support.inner.classes.but.it.is.used.in.parameter.initializer")); - } - final PsiAnonymousClass anonymousClass = expression.getAnonymousClass(); - if (anonymousClass != null) { - conflicts.putValue(anonymousClass, GroovyRefactoringBundle.message("groovy.does.not.support.anonymous.classes.but.it.is.used.in.parameter.initializer")); - } - } } public boolean processChangeMethodUsage(IntroduceParameterData data, UsageInfo usage, UsageInfo[] usages) throws IncorrectOperationException { @@ -117,29 +95,33 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP } } - PsiMethod method = PsiTreeUtil.getParentOfType(argList, PsiMethod.class); - if (method!=null && isMethodInUsages(method, usages, data)) { + + GrClosureSignature signature = GrClosureSignatureUtil.createSignature(callExpression); + if (signature == null) signature = GrClosureSignatureUtil.createSignature(data.getMethodToSearchFor(), PsiSubstitutor.EMPTY); + + final GrClosureSignatureUtil.ArgInfo[] actualArgs = + GrClosureSignatureUtil.mapParametersToArguments(signature, argList, callExpression, callExpression.getClosureArguments(), true); + + if (method != null && IntroduceParameterUtil.isMethodInUsages(data, method, usages)) { argList.addAfter(GroovyPsiElementFactory.getInstance(data.getProject()).createExpressionFromText(data.getParameterName()), anchor); } else { - ChangeContextUtil.encodeContextInfo(data.getParameterInitializer(), true); - final GrExpression grInitializer = GroovyRefactoringUtil.convertJavaExpr2GroovyExpr(data.getParameterInitializer()); - GrExpression newArg = (GrExpression)argList.addAfter(grInitializer, anchor); - new OldReferencesResolver(callExpression, newArg, data.getMethodToReplaceIn(), data.getReplaceFieldsWithGetters(), - data.getParameterInitializer()).resolve(); - ChangeContextUtil.clearContextInfo(data.getParameterInitializer()); + PsiElement initializer = ExpressionConverter + .getExpression(data.getParameterInitializer().getExpression(), GroovyFileType.GROOVY_LANGUAGE, data.getProject()); + LOG.assertTrue(initializer instanceof GrExpression); + + GrExpression newArg = (GrExpression)argList.addAfter(initializer, anchor); + new OldReferencesResolver(callExpression, newArg, data.getMethodToReplaceIn(), data.getReplaceFieldsWithGetters(), initializer, + signature, actualArgs).resolve(); + ChangeContextUtil.clearContextInfo(initializer); } - - removeParametersFromCall(callExpression.getArgumentList(), data.getParametersToRemove()); - return false; - } - - private static boolean isMethodInUsages(PsiMethod method, UsageInfo[] usages, IntroduceParameterData data) { - PsiManager manager=PsiManager.getInstance(data.getProject()); - for (UsageInfo info : usages) { - if (!(info instanceof DefaultConstructorImplicitUsageInfo) && manager.areElementsEquivalent(info.getElement(), method)) return true; + if (actualArgs == null) { + removeParamsFromUnresolvedCall(callExpression, data); + } + else { + removeParametersFromCall(actualArgs, data.getParametersToRemove()); } return false; } @@ -156,16 +138,64 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP return anchor; } - private static void removeParametersFromCall(final GrArgumentList argList, TIntArrayList parametersToRemove) { - final GrExpression[] exprs = argList.getExpressionArguments(); - parametersToRemove.forEachDescending(new TIntProcedure() { + private static void removeParametersFromCall(final GrClosureSignatureUtil.ArgInfo[] actualArgs,final TIntArrayList parametersToRemove) { + parametersToRemove.forEach(new TIntProcedure() { public boolean execute(final int paramNum) { try { - - exprs[paramNum].delete(); + final GrClosureSignatureUtil.ArgInfo actualArg = actualArgs[paramNum]; + for (PsiElement arg : actualArg.args) { + arg.delete(); + } } catch (IncorrectOperationException e) { + LOG.error(e); + } + return true; + } + }); + } + private static void removeParamsFromUnresolvedCall(GrCall callExpression, IntroduceParameterData data) { + final GrExpression[] arguments = callExpression.getExpressionArguments(); + final GrClosableBlock[] closureArguments = callExpression.getClosureArguments(); + final GrNamedArgument[] namedArguments = callExpression.getNamedArguments(); + + final boolean hasNamedArgs; + if (namedArguments.length > 0) { + final PsiMethod method = data.getMethodToSearchFor(); + final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length > 0) { + final PsiType type = parameters[0].getType(); + hasNamedArgs = InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_UTIL_MAP); + } + else { + hasNamedArgs = false; + } + } + else { + hasNamedArgs = false; + } + + data.getParametersToRemove().forEachDescending(new TIntProcedure() { + public boolean execute(int paramNum) { + try { + if (paramNum == 0 && hasNamedArgs) { + for (GrNamedArgument namedArgument : namedArguments) { + namedArgument.delete(); + } + } + else { + if (hasNamedArgs) paramNum--; + if (paramNum < arguments.length) { + arguments[paramNum].delete(); + } + else if (paramNum < arguments.length + closureArguments.length) { + closureArguments[paramNum - arguments.length].delete(); + } + } + } + catch (IncorrectOperationException e) { + LOG.error(e); } return true; } @@ -178,7 +208,6 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(data.getParameterName(), method.getBlock()); final MethodJavaDocHelper javaDocHelper = new MethodJavaDocHelper(method); - PsiManager manager = method.getManager(); GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(method.getProject()); final PsiParameter[] parameters = method.getParameterList().getParameters(); @@ -193,18 +222,21 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP param.delete(); } catch (IncorrectOperationException e) { -// LOG.error(e); + LOG.error(e); } return true; } }); - PsiParameter parameter = factory.createParameter(data.getParameterName(), data.getForcedType().getCanonicalText(), method); - PsiUtil.setModifierProperty(parameter, PsiModifier.FINAL, data.isDeclareFinal()); + final PsiType forcedType = data.getForcedType(); + final String typeText = forcedType.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) ? null : forcedType.getCanonicalText(); + + GrParameter parameter = factory.createParameter(data.getParameterName(), typeText, method); + parameter.getModifierList().setModifierProperty(GrModifier.FINAL, data.isDeclareFinal()); final PsiParameter anchorParameter = getAnchorParameter(method); final GrParameterList parameterList = method.getParameterList(); - parameter = (PsiParameter)parameterList.addAfter(parameter, anchorParameter); - JavaCodeStyleManager.getInstance(manager.getProject()).shortenClassReferences(parameter); + parameter = (GrParameter)parameterList.addAfter(parameter, anchorParameter); + PsiUtil.shortenReferences(parameter); final PsiDocTag tagForAnchorParameter = javaDocHelper.getTagForParameter(anchorParameter); javaDocHelper.addParameterAfter(data.getParameterName(), tagForAnchorParameter); @@ -224,8 +256,6 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP anchorParameter = length > 0 ? parameters[length - 1] : null; } else { -// LOG.assertTrue(length > 0); -// LOG.assertTrue(parameters[length - 1].isVarArgs()); anchorParameter = length > 1 ? parameters[length - 2] : null; } return anchorParameter; @@ -238,9 +268,8 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP GrMethod constructor = factory.createConstructorFromText(aClass.getName(), ArrayUtil.EMPTY_STRING_ARRAY, ArrayUtil.EMPTY_STRING_ARRAY, "{}"); constructor = (GrMethod)aClass.add(constructor); - PsiUtil.setModifierProperty(constructor, VisibilityUtil.getVisibilityModifier(aClass.getModifierList()), true); + constructor.getModifierList().setModifierProperty(VisibilityUtil.getVisibilityModifier(aClass.getModifierList()), true); processAddSuperCall(data, new UsageInfo(constructor), usages); -// constructor = (GrMethod)CodeStyleManager.getInstance(data.getProject()).reformat(constructor); return false; } @@ -252,17 +281,16 @@ public class GroovyIntroduceParameterMethodUsagesProcessor implements IntroduceP final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(data.getProject()); - GrExpression superCall = (GrExpression)factory.createStatementFromText("super();"); - superCall = (GrExpression)CodeStyleManager.getInstance(data.getProject()).reformat(superCall); + GrConstructorInvocation superCall = factory.createConstructorInvocation("super();"); GrOpenBlock body = constructor.getBlock(); final GrStatement[] statements = body.getStatements(); if (statements.length > 0) { - superCall = (GrExpression)body.addStatementBefore(superCall, statements[0]); + superCall = (GrConstructorInvocation)body.addStatementBefore(superCall, statements[0]); } else { - superCall = (GrExpression)body.addStatementBefore(superCall, null); + superCall = (GrConstructorInvocation)body.addStatementBefore(superCall, null); } - processChangeMethodUsage(data, new UsageInfo(((GrMethodCallExpression)superCall).getInvokedExpression()), usages); + processChangeMethodUsage(data, new UsageInfo(superCall), usages); return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java index 0c5d6f0e10a4..f7a405e616e4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/introduce/parameter/java2groovy/OldReferencesResolver.java @@ -16,22 +16,28 @@ package org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; -import com.intellij.psi.impl.source.tree.CompositePsiElement; -import com.intellij.psi.util.PropertyUtil; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.IntroduceParameterRefactoring; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureSignature; +import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; +import java.util.List; import java.util.Map; import java.util.Set; @@ -43,28 +49,36 @@ import static com.intellij.codeInsight.ChangeContextUtil.*; */ public class OldReferencesResolver { + private static final Logger LOG = + Logger.getInstance("#org.jetbrains.plugins.groovy.refactoring.introduce.parameter.java2groovy.OldReferencesResolver"); + private final GrCall myContext; private final GrExpression myExpr; private final HashMap myTempVars; private final GrExpression myInstanceRef; - private final GrExpression[] myActualArgs; + private final GrClosureSignatureUtil.ArgInfo[] myActualArgs; private final PsiMethod myMethodToReplaceIn; private final Project myProject; private final int myReplaceFieldsWithGetters; - private final PsiExpression myParameterInitializer; + private final PsiElement myParameterInitializer; private final PsiManager myManager; + private final PsiParameter[] myParameters; + private final GrClosureSignature mySignature; public OldReferencesResolver(GrCall context, GrExpression expr, PsiMethod methodToReplaceIn, int replaceFieldsWithGetters, - PsiExpression parameterInitializer) throws IncorrectOperationException { + PsiElement parameterInitializer, + final GrClosureSignature signature, + final GrClosureSignatureUtil.ArgInfo[] actualArgs) throws IncorrectOperationException { myContext = context; myExpr = expr; myReplaceFieldsWithGetters = replaceFieldsWithGetters; myParameterInitializer = parameterInitializer; myTempVars = new HashMap(); - myActualArgs = myContext.getArgumentList().getExpressionArguments(); + mySignature = signature; + myActualArgs = actualArgs; myMethodToReplaceIn = methodToReplaceIn; myProject = myContext.getProject(); myManager = myContext.getManager(); @@ -82,6 +96,7 @@ public class OldReferencesResolver { else { myInstanceRef = null; } + myParameters = myMethodToReplaceIn.getParameterList().getParameters(); } public void resolve() throws IncorrectOperationException { @@ -104,15 +119,15 @@ public class OldReferencesResolver { GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(myProject); PsiElement newExpr = expr; // references continue being resolved in the children of newExpr - if (oldExpr instanceof PsiReferenceExpression) { - final PsiReferenceExpression oldRef = (PsiReferenceExpression)oldExpr; + if (oldExpr instanceof GrReferenceExpression) { + final GrReferenceExpression oldRef = (GrReferenceExpression)oldExpr; newExpr = newExpr.replace(decodeReferenceExpression((GrReferenceExpression)newExpr, oldRef)); //newExpr = ((GrReferenceExpression)newExpr).getReferenceNameElement(); - final JavaResolveResult adv = oldRef.advancedResolve(false); + final GroovyResolveResult adv = oldRef.advancedResolve(); final PsiElement scope = getClassContainingResolve(adv); - final PsiElement owner = PsiTreeUtil.getParentOfType(oldExpr, PsiClass.class); + final PsiElement owner = PsiTreeUtil.getContextOfType(oldExpr, PsiClass.class); - if (owner != null && scope != null && PsiTreeUtil.isAncestor(owner, scope, false)) { + if (owner != null && scope != null && PsiTreeUtil.isContextAncestor(owner, scope, false)) { final PsiElement subj = adv.getElement(); @@ -125,7 +140,7 @@ public class OldReferencesResolver { int index = parameterList.getParameterIndex((PsiParameter)subj); if (index < 0) return; if (index < parameters.length) { - GrExpression actualArg = myActualArgs[index]; + GrExpression actualArg = getActualArg(index); int copyingSafetyLevel = GroovyRefactoringUtil.verifySafeCopyExpression(actualArg); if (copyingSafetyLevel == RefactoringUtil.EXPR_COPY_PROHIBITED) { actualArg = factory.createExpressionFromText(getTempVar(actualArg)); @@ -139,15 +154,24 @@ public class OldReferencesResolver { boolean isStatic = subj instanceof PsiField && ((PsiField)subj).hasModifierProperty(PsiModifier.STATIC) || subj instanceof PsiMethod && ((PsiMethod)subj).hasModifierProperty(PsiModifier.STATIC); - if (myInstanceRef != null && !isStatic) { - String name = ((PsiNamedElement)subj).getName(); - GrReferenceExpression newRef = (GrReferenceExpression)factory.createExpressionFromText("a." + name); - GrExpression instanceRef = getInstanceRef(factory); - newRef.getQualifierExpression().replace(instanceRef); - newRef = (GrReferenceExpression)CodeStyleManager.getInstance(myProject).reformat(newRef); - - newRef = (GrReferenceExpression)newExpr.replace(newRef); - newExpr = newRef.getReferenceNameElement(); + String name = ((PsiNamedElement)subj).getName(); + boolean shouldBeAt = subj instanceof PsiField && + !PsiTreeUtil.isAncestor(((PsiMember)subj).getContainingClass(), newExpr, true) && + GroovyPropertyUtils.findGetterForField((PsiField)subj) != null; + final GrReferenceExpression fromText = factory.createReferenceExpressionFromText("qualifier." + (shouldBeAt ? "@" : "") + name); + if (isStatic) { + final GrReferenceExpression qualifier = factory.createReferenceElementForClass(((PsiMember)subj).getContainingClass()); + newExpr = newExpr.replace(fromText); + ((GrReferenceExpression)newExpr).setQualifier(qualifier); + newExpr = ((GrReferenceExpression)newExpr).getReferenceNameElement(); + } + else { + if (myInstanceRef != null) { + GrExpression instanceRef = getInstanceRef(factory); + fromText.setQualifier(instanceRef); + newExpr = newExpr.replace(fromText); + newExpr = ((GrReferenceExpression)newExpr).getReferenceNameElement(); + } } } @@ -163,16 +187,15 @@ public class OldReferencesResolver { } } } - else if (oldExpr instanceof PsiThisExpression && - (((PsiThisExpression)oldExpr).getQualifier() == null || - myManager - .areElementsEquivalent(((PsiThisExpression)oldExpr).getQualifier().resolve(), myMethodToReplaceIn.getContainingClass()))) { + else if (oldExpr instanceof GrThisReferenceExpression && + (((GrThisReferenceExpression)oldExpr).getQualifier() == null || myManager.areElementsEquivalent( + ((GrThisReferenceExpression)oldExpr).getQualifier().resolve(), myMethodToReplaceIn.getContainingClass()))) { if (myInstanceRef != null) { newExpr.replace(getInstanceRef(factory)); } return; } - else if (oldExpr instanceof PsiSuperExpression && ((PsiSuperExpression)oldExpr).getQualifier() == null) { + else if (oldExpr instanceof GrSuperReferenceExpression && ((GrSuperReferenceExpression)oldExpr).getQualifier() == null) { if (myInstanceRef != null) { newExpr.replace(getInstanceRef(factory)); } @@ -197,23 +220,20 @@ public class OldReferencesResolver { PsiElement[] oldChildren = oldExpr.getChildren(); PsiElement[] newChildren = newExpr.getChildren(); - if (oldExpr instanceof PsiNewExpression && newExpr instanceof GrNewExpression) { //special new-expression case + if (oldExpr instanceof GrNewExpression && newExpr instanceof GrNewExpression) { //special new-expression case resolveOldReferences(((GrNewExpression)newExpr).getReferenceElement(), - ((PsiNewExpression)oldExpr).getClassOrAnonymousClassReference()); + ((GrNewExpression)oldExpr).getReferenceElement()); - resolveOldReferences(((GrNewExpression)newExpr).getArgumentList(), ((PsiNewExpression)oldExpr).getArgumentList()); + resolveOldReferences(((GrNewExpression)newExpr).getArgumentList(), ((GrNewExpression)oldExpr).getArgumentList()); if (newChildren[1] instanceof GrArrayDeclaration) { - int j = 3; //array dimension expressions may occur since 3 position for (GrExpression expression : ((GrArrayDeclaration)newChildren[1]).getBoundExpressions()) { - while (!(oldChildren[j] instanceof CompositePsiElement)) j++; - resolveOldReferences(expression, oldChildren[j]); - j++; + resolveOldReferences(expression, oldChildren[1]); } } } else { - if (oldExpr instanceof PsiReferenceExpression && newExpr instanceof GrReferenceExpression) { - final PsiExpression oldQualifier = ((PsiReferenceExpression)oldExpr).getQualifierExpression(); + if (oldExpr instanceof GrReferenceExpression && newExpr instanceof GrReferenceExpression) { + final GrExpression oldQualifier = ((GrReferenceExpression)oldExpr).getQualifierExpression(); final GrExpression newQualifier = ((GrReferenceExpression)newExpr).getQualifierExpression(); if (oldQualifier != null && newQualifier != null) { resolveOldReferences(newQualifier, oldQualifier); @@ -221,25 +241,41 @@ public class OldReferencesResolver { } } - int oldCount = countOldChildren(oldChildren); - if (oldCount == newChildren.length) { - int j = 0; + if (oldChildren.length == newChildren.length) { for (int i = 0; i < newChildren.length; i++) { - while (!(oldChildren[j] instanceof CompositePsiElement)) j++; - resolveOldReferences(newChildren[i], oldChildren[j]); - j++; + resolveOldReferences(newChildren[i], oldChildren[i]); } } - } } - private static int countOldChildren(PsiElement[] children) { - int count = 0; - for (PsiElement child : children) { - if (child instanceof CompositePsiElement) count++; + @NotNull + private GrExpression getActualArg(int index) { + if (myActualArgs == null) { + final GrExpression[] arguments = myContext.getArgumentList().getExpressionArguments(); + if (index < arguments.length) return arguments[index]; + index -= arguments.length; + final GrClosableBlock[] closureArguments = myContext.getClosureArguments(); + if (index < closureArguments.length) return closureArguments[index]; + throw new IncorrectOperationException("fail :("); + } + + final GrClosureSignatureUtil.ArgInfo argInfo = myActualArgs[index]; + final List args = argInfo.args; + if (argInfo.isMultiArg) { + return GroovyRefactoringUtil.generateArgFromMultiArg(mySignature.getSubstitutor(), args, myParameters[index].getType(), + myContext.getProject()); + } + else if (args.size() == 0) { + final PsiParameter parameter = myParameters[index]; + LOG.assertTrue(parameter instanceof GrParameter); + final GrExpression initializer = ((GrParameter)parameter).getInitializerGroovy(); + LOG.assertTrue(initializer != null); + return (GrExpression)initializer.copy(); + } + else { + return (GrExpression)args.get(0); } - return count; } private GrExpression getInstanceRef(GroovyPsiElementFactory factory) throws IncorrectOperationException { @@ -264,19 +300,18 @@ public class OldReferencesResolver { } } - private PsiElement replaceFieldWithGetter(PsiElement expr, PsiField psiField) throws IncorrectOperationException { + private static PsiElement replaceFieldWithGetter(PsiElement expr, PsiField psiField) throws IncorrectOperationException { if (RefactoringUtil.isAssignmentLHS(expr)) { // todo: warning return expr; } PsiElement newExpr = expr; - PsiMethod getterPrototype = PropertyUtil.generateGetterPrototype(psiField); - - PsiMethod getter = psiField.getContainingClass().findMethodBySignature(getterPrototype, true); + PsiMethod getter = GroovyPropertyUtils.findGetterForField(psiField); if (getter != null) { if (JavaPsiFacade.getInstance(psiField.getProject()).getResolveHelper().isAccessible(getter, newExpr, null)) { + GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(newExpr.getProject()); String id = getter.getName(); final PsiElement parent = newExpr.getParent(); @@ -287,14 +322,18 @@ public class OldReferencesResolver { qualifier = qualifierExpression.getText(); } } - GrMethodCallExpression getterCall = - (GrMethodCallExpression)factory.createExpressionFromText((qualifier != null ? qualifier + "." : "") + id + "()"); - getterCall = (GrMethodCallExpression)CodeStyleManager.getInstance(myProject).reformat(getterCall); + GrExpression getterCall; + if (PsiTreeUtil.isAncestor(psiField.getContainingClass(), expr, true)) { + getterCall = factory.createExpressionFromText((qualifier != null ? qualifier + "." : "") + id + "()"); + } + else { + getterCall = factory.createExpressionFromText((qualifier != null ? qualifier + "." : "") + psiField.getName()); + } if (parent != null) { newExpr = parent.replace(getterCall); } else { - newExpr = getterCall; + newExpr = expr.replace(getterCall); } } else { @@ -306,25 +345,25 @@ public class OldReferencesResolver { } @Nullable - private static PsiElement getClassContainingResolve(final JavaResolveResult result) { + private static PsiElement getClassContainingResolve(final GroovyResolveResult result) { final PsiElement elem = result.getElement(); if (elem != null) { - if (elem instanceof PsiLocalVariable || elem instanceof PsiParameter) { - return PsiTreeUtil.getParentOfType(elem, PsiClass.class); + if (elem instanceof PsiMember) { + return ((PsiMember)elem).getContainingClass(); } else { - return result.getCurrentFileResolveScope(); + return PsiTreeUtil.getParentOfType(elem, PsiClass.class); } } return null; } - private static GrReferenceExpression decodeReferenceExpression(GrReferenceExpression newExpr, PsiReferenceExpression refExpr) + private static GrReferenceExpression decodeReferenceExpression(GrReferenceExpression newExpr, GrReferenceExpression refExpr) throws IncorrectOperationException { PsiManager manager = refExpr.getManager(); GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(manager.getProject()); - PsiExpression qualifier = refExpr.getQualifierExpression(); + GrExpression qualifier = refExpr.getQualifier(); if (qualifier == null) { PsiMember refMember = refExpr.getCopyableUserData(REF_MEMBER_KEY); refExpr.putCopyableUserData(REF_MEMBER_KEY, null); @@ -358,29 +397,29 @@ public class OldReferencesResolver { return newExpr; } - private static boolean canRemoveQualifier(PsiReferenceExpression refExpr) { + private static boolean canRemoveQualifier(GrReferenceExpression refExpr) { try { - PsiExpression qualifier = refExpr.getQualifierExpression(); - if (!(qualifier instanceof PsiReferenceExpression)) return false; + GrExpression qualifier = refExpr.getQualifier(); + if (!(qualifier instanceof GrReferenceExpression)) return false; - PsiElement qualifierRefElement = ((PsiReferenceExpression)qualifier).resolve(); + PsiElement qualifierRefElement = ((GrReferenceExpression)qualifier).resolve(); if (!(qualifierRefElement instanceof PsiClass)) return false; PsiElement refElement = refExpr.resolve(); if (refElement == null) return false; - PsiElementFactory factory = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory(); + final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(refExpr.getProject()); - if (refExpr.getParent() instanceof PsiMethodCallExpression) { - PsiMethodCallExpression methodCall = (PsiMethodCallExpression)refExpr.getParent(); - PsiMethodCallExpression newMethodCall = - (PsiMethodCallExpression)factory.createExpressionFromText(refExpr.getReferenceName() + "()", refExpr); + if (refExpr.getParent() instanceof GrMethodCallExpression) { + GrMethodCallExpression methodCall = (GrMethodCallExpression)refExpr.getParent(); + GrMethodCallExpression newMethodCall = + (GrMethodCallExpression)factory.createExpressionFromText(refExpr.getReferenceName() + "()", refExpr); newMethodCall.getArgumentList().replace(methodCall.getArgumentList()); - PsiElement newRefElement = newMethodCall.getMethodExpression().resolve(); + PsiElement newRefElement = ((GrReferenceExpression)newMethodCall.getInvokedExpression()).resolve(); return refElement.equals(newRefElement); } else { - PsiReferenceExpression newRefExpr = (PsiReferenceExpression)factory.createExpressionFromText(refExpr.getReferenceName(), refExpr); + GrReferenceExpression newRefExpr = (GrReferenceExpression)factory.createExpressionFromText(refExpr.getReferenceName(), refExpr); PsiElement newRefElement = newRefExpr.resolve(); return refElement.equals(newRefElement); } @@ -389,4 +428,4 @@ public class OldReferencesResolver { return false; } } -} \ No newline at end of file +} diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index b75b750056fe..13b2c755b924 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -158,6 +158,8 @@ + + @@ -192,7 +194,7 @@ - +