From dfc7e8cb61b5a6672a2382497a397e345ea0fe0b Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 10 Dec 2014 15:15:55 +0100 Subject: [PATCH] introduce parameter: allow to introduce parameter from code block (IDEA-81166) --- .../FunctionalInterfaceSuggester.java | 37 +++ .../extractMethod/ExtractMethodProcessor.java | 20 +- .../InplaceIntroduceParameterPopup.java | 2 - .../IntroduceParameterHandler.java | 264 +++++++++++++++--- .../IntroduceVariableBase.java | 11 +- .../afterIntConsumer.java | 18 ++ .../afterSampleRunnable.java | 16 ++ .../beforeIntConsumer.java | 12 + .../beforeSampleRunnable.java | 12 + .../IntroduceFunctionalParameterTest.java | 75 +++++ 10 files changed, 417 insertions(+), 50 deletions(-) create mode 100644 java/java-tests/testData/refactoring/introduceFunctionalParameter/afterIntConsumer.java create mode 100644 java/java-tests/testData/refactoring/introduceFunctionalParameter/afterSampleRunnable.java create mode 100644 java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeIntConsumer.java create mode 100644 java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeSampleRunnable.java create mode 100644 java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java diff --git a/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java b/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java index 4db78a9d1cfe..65c10af03f6d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java +++ b/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java @@ -21,6 +21,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.AnnotatedMembersSearch; import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.NullableFunction; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; @@ -43,6 +44,42 @@ public class FunctionalInterfaceSuggester { }); } + public static Collection suggestFunctionalInterfaces(final @NotNull PsiMethod method) { + if (method.isConstructor()) { + return Collections.emptyList(); + } + + return suggestFunctionalInterfaces(method, new NullableFunction() { + @Nullable + @Override + public PsiType fun(PsiClass aClass) { + final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(aClass); + if (interfaceMethod != null) { + final PsiParameter[] parameters = method.getParameterList().getParameters(); + final PsiParameter[] interfaceMethodParameters = interfaceMethod.getParameterList().getParameters(); + if (parameters.length != interfaceMethodParameters.length) { + return null; + } + for (int i = 0; i < interfaceMethodParameters.length; i++) { + if (!TypeConversionUtil.isAssignable(parameters[i].getType(), interfaceMethodParameters[i].getType())) { + return null; + } + } + + final PsiType returnType = method.getReturnType(); + if (!TypeConversionUtil.isAssignable(interfaceMethod.getReturnType(), returnType)) { + return null; + } + + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(aClass.getProject()); + final PsiType type = elementFactory.createType(aClass, PsiSubstitutor.EMPTY); + return type; + } + return null; + } + }); + } + private static Collection suggestFunctionalInterfaces(final @NotNull T element, final NullableFunction acceptanceChecker) { final Project project = element.getProject(); final PsiClass functionalInterfaceClass = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_FUNCTIONAL_INTERFACE, diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java index 36866392bc54..7e4a5b46028b 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java @@ -622,7 +622,7 @@ public class ExtractMethodProcessor implements MatchProvider { final List map = ContainerUtil.map(filter, new Function() { @Override public PsiExpression fun(PsiStatement statement) { - return ((PsiReturnStatement) statement).getReturnValue(); + return ((PsiReturnStatement)statement).getReturnValue(); } }); return map.toArray(new PsiExpression[map.size()]); @@ -630,7 +630,7 @@ public class ExtractMethodProcessor implements MatchProvider { private Nullness initNullness() { if (!PsiUtil.isLanguageLevel5OrHigher(myElements[0]) || PsiUtil.resolveClassInType(myReturnType) == null) return null; - final PsiMethod emptyMethod = (PsiMethod)myTargetClass.copy().add(generateEmptyMethod(getThrownExceptions(), isStatic(), "name")); + final PsiMethod emptyMethod = (PsiMethod)myTargetClass.copy().add(generateEmptyMethod("name")); prepareMethodBody(emptyMethod, false); final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject); final PsiClass nullableAnnotationClass = JavaPsiFacade.getInstance(myProject) @@ -833,7 +833,7 @@ public class ExtractMethodProcessor implements MatchProvider { public void doExtract() throws IncorrectOperationException { - PsiMethod newMethod = generateEmptyMethod(getThrownExceptions(), isStatic()); + PsiMethod newMethod = generateEmptyMethod(); myExpression = myInputVariables.replaceWrappedReferences(myElements, myExpression); renameInputVariables(); @@ -1239,7 +1239,7 @@ public class ExtractMethodProcessor implements MatchProvider { return match.replace(myExtractedMethod, methodCallExpression, myOutputVariable); } - private void deleteExtracted() throws IncorrectOperationException { + protected void deleteExtracted() throws IncorrectOperationException { if (myEnclosingBlockStatement == null) { myElements[0].getParent().deleteChildRange(myElements[0], myElements[myElements.length - 1]); } @@ -1284,20 +1284,18 @@ public class ExtractMethodProcessor implements MatchProvider { return myReturnType; } - private PsiMethod generateEmptyMethod(PsiClassType[] exceptions, boolean isStatic) throws IncorrectOperationException { - return generateEmptyMethod(exceptions, isStatic, myMethodName); + private PsiMethod generateEmptyMethod() throws IncorrectOperationException { + return generateEmptyMethod(myMethodName); } - private PsiMethod generateEmptyMethod(PsiClassType[] exceptions, - boolean isStatic, - String methodName) throws IncorrectOperationException { + public PsiMethod generateEmptyMethod(String methodName) throws IncorrectOperationException { PsiMethod newMethod; if (myIsChainedConstructor) { newMethod = myElementFactory.createConstructor(); } else { newMethod = myElementFactory.createMethod(methodName, myReturnType); - PsiUtil.setModifierProperty(newMethod, PsiModifier.STATIC, isStatic); + PsiUtil.setModifierProperty(newMethod, PsiModifier.STATIC, isStatic()); } PsiUtil.setModifierProperty(newMethod, myMethodVisibility, true); if (getTypeParameterList() != null) { @@ -1336,7 +1334,7 @@ public class ExtractMethodProcessor implements MatchProvider { } PsiReferenceList throwsList = newMethod.getThrowsList(); - for (PsiClassType exception : exceptions) { + for (PsiClassType exception : getThrownExceptions()) { throwsList.add(JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createReferenceElementByType(exception)); } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/InplaceIntroduceParameterPopup.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/InplaceIntroduceParameterPopup.java index f36212e505eb..5528a93cae6b 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/InplaceIntroduceParameterPopup.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/InplaceIntroduceParameterPopup.java @@ -38,7 +38,6 @@ import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.ui.JBColor; -import com.intellij.usageView.UsageInfo; import gnu.trove.TIntArrayList; import gnu.trove.TIntProcedure; import org.jetbrains.annotations.NotNull; @@ -66,7 +65,6 @@ public class InplaceIntroduceParameterPopup extends AbstractJavaInplaceIntroduce InplaceIntroduceParameterPopup(final Project project, final Editor editor, - final List classMemberRefs, final TypeSelectorManagerImpl typeSelectorManager, final PsiExpression expr, final PsiLocalVariable localVar, diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java index 3c844d0ec052..336cb3ca2509 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java @@ -25,12 +25,18 @@ package com.intellij.refactoring.introduceParameter; import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.codeInsight.FunctionalInterfaceSuggester; import com.intellij.codeInsight.completion.JavaCompletionUtil; +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.ide.util.PsiClassListCellRenderer; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.markup.*; @@ -40,17 +46,24 @@ import com.intellij.openapi.ui.popup.JBPopupAdapter; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.LightweightWindowEvent; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.IntroduceHandlerBase; import com.intellij.refactoring.IntroduceParameterRefactoring; import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.extractMethod.AbstractExtractDialog; +import com.intellij.refactoring.extractMethod.ExtractMethodProcessor; +import com.intellij.refactoring.extractMethod.InputVariables; +import com.intellij.refactoring.extractMethod.PrepareFailedException; import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer; import com.intellij.refactoring.introduceField.ElementToWorkOn; import com.intellij.refactoring.ui.MethodCellRenderer; @@ -58,15 +71,19 @@ import com.intellij.refactoring.ui.NameSuggestionsGenerator; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.refactoring.util.VariableData; import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBList; import com.intellij.usageView.UsageInfo; import com.intellij.util.ArrayUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.PairConsumer; import gnu.trove.TIntArrayList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import javax.swing.*; import javax.swing.event.ListSelectionEvent; @@ -75,8 +92,7 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; -import java.util.ArrayList; -import java.util.Collections; +import java.util.*; import java.util.List; @@ -86,7 +102,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { private JBPopup myEnclosingMethodsPopup; private InplaceIntroduceParameterPopup myInplaceIntroduceParameterPopup; - public void invoke(@NotNull final Project project, final Editor editor, PsiFile file, DataContext dataContext) { + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { PsiDocumentManager.getInstance(project).commitAllDocuments(); editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); ElementToWorkOn.processElementToWorkOn(editor, file, REFACTORING_NAME, HelpID.INTRODUCE_PARAMETER, project, new ElementToWorkOn.ElementsProcessor() { @@ -97,7 +113,10 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { @Override public void pass(final ElementToWorkOn elementToWorkOn) { - if (elementToWorkOn == null) return; + if (elementToWorkOn == null) { + introduceStrategy(project, editor, file); + return; + } final PsiExpression expr = elementToWorkOn.getExpression(); final PsiLocalVariable localVar = elementToWorkOn.getLocalVariable(); @@ -173,30 +192,36 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { if (!CommonRefactoringUtil.checkReadOnlyStatus(project, method)) return false; final Introducer introducer = new Introducer(project, expr, localVar, editor); - final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode(); - if (validEnclosingMethods.size() == 1 || unitTestMode) { - final PsiMethod methodToIntroduceParameterTo = validEnclosingMethods.get(0); - if (methodToIntroduceParameterTo.findDeepestSuperMethod() == null || unitTestMode) { - introducer.introduceParameter(methodToIntroduceParameterTo, methodToIntroduceParameterTo); - return true; - } + final AbstractInplaceIntroducer inplaceIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); + if (inplaceIntroducer instanceof InplaceIntroduceParameterPopup) { + final InplaceIntroduceParameterPopup introduceParameterPopup = (InplaceIntroduceParameterPopup)inplaceIntroducer; + introducer.introduceParameter(introduceParameterPopup.getMethodToIntroduceParameter(), + introduceParameterPopup.getMethodToSearchFor()); + return true; } - chooseMethodToIntroduceParameter(editor, validEnclosingMethods, introducer); + chooseMethodToIntroduceParameter(editor, validEnclosingMethods, new PairConsumer() { + @Override + public void consume(PsiMethod methodToSearchIn, PsiMethod methodToSearchFor) { + introducer.introduceParameter(methodToSearchIn, methodToSearchFor); + } + }); return true; } private void chooseMethodToIntroduceParameter(final Editor editor, final List validEnclosingMethods, - final Introducer introducer) { - final AbstractInplaceIntroducer inplaceIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); - if (inplaceIntroducer instanceof InplaceIntroduceParameterPopup) { - final InplaceIntroduceParameterPopup introduceParameterPopup = (InplaceIntroduceParameterPopup)inplaceIntroducer; - introducer.introduceParameter(introduceParameterPopup.getMethodToIntroduceParameter(), - introduceParameterPopup.getMethodToSearchFor()); - return; + final PairConsumer consumer) { + final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode(); + if (validEnclosingMethods.size() == 1 || unitTestMode) { + final PsiMethod methodToIntroduceParameterTo = validEnclosingMethods.get(0); + if (methodToIntroduceParameterTo.findDeepestSuperMethod() == null || unitTestMode) { + consumer.consume(methodToIntroduceParameterTo, methodToIntroduceParameterTo); + return; + } } + final JPanel panel = new JPanel(new BorderLayout()); final JCheckBox superMethod = new JCheckBox("Refactor super method", true); superMethod.setMnemonic('U'); @@ -235,7 +260,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { ? methodToSearchIn.findDeepestSuperMethod() : methodToSearchIn; Runnable runnable = new Runnable() { public void run() { - introducer.introduceParameter(methodToSearchIn, methodToSearchFor); + consumer.consume(methodToSearchIn, methodToSearchFor); } }; IdeFocusManager.findInstance().doWhenFocusSettlesDown(runnable); @@ -377,8 +402,6 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { } public void introduceParameter(PsiMethod method, PsiMethod methodToSearchFor) { - if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, methodToSearchFor)) return; - PsiExpression[] occurences; if (myExpr != null) { occurences = new ExpressionOccurrenceManager(myExpr, method, null).findExpressionOccurrences(); @@ -412,15 +435,6 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { } } - List localVars = new ArrayList(); - List classMemberRefs = new ArrayList(); - List params = new ArrayList(); - - - if (myExpr != null) { - Util.analyzeExpression(myExpr, localVars, classMemberRefs, params); - } - final String propName = myLocalVar != null ? JavaCodeStyleManager .getInstance(myProject).variableNameToPropertyName(myLocalVar.getName(), VariableKind.LOCAL_VARIABLE) : null; @@ -432,7 +446,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { if (isInplaceAvailableOnDataContext && activeIntroducer == null) { myInplaceIntroduceParameterPopup = - new InplaceIntroduceParameterPopup(myProject, myEditor, classMemberRefs, + new InplaceIntroduceParameterPopup(myProject, myEditor, createTypeSelectorManager(occurences, initializerType), myExpr, myLocalVar, method, methodToSearchFor, occurences, getParamsToRemove(method, occurences), @@ -454,6 +468,12 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { if (myEditor != null) { RefactoringUtil.highlightAllOccurrences(myProject, occurences, myEditor); } + + final List classMemberRefs = new ArrayList(); + if (myExpr != null) { + Util.analyzeExpression(myExpr, new ArrayList(), classMemberRefs, new ArrayList()); + } + final IntroduceParameterDialog dialog = new IntroduceParameterDialog(myProject, classMemberRefs, occurences, myLocalVar, myExpr, createNameSuggestionGenerator(myExpr, propName, myProject, enteredName), @@ -486,4 +506,184 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { public AbstractInplaceIntroducer getInplaceIntroducer() { return myInplaceIntroduceParameterPopup; } + + @TestOnly + public boolean introduceStrategy(final Project project, final Editor editor, PsiFile file) { + final SelectionModel selectionModel = editor.getSelectionModel(); + if (selectionModel.hasSelection()) { + final PsiElement[] elements = CodeInsightUtil + .findStatementsInRange(file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()); + if (elements.length > 0) { + final AbstractInplaceIntroducer inplaceIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); + if (inplaceIntroducer instanceof InplaceIntroduceParameterPopup) { + return false; + } + final List enclosingMethods = getEnclosingMethods(Util.getContainingMethod(elements[0])); + if (enclosingMethods.isEmpty()) { + return false; + } + + final MyExtractMethodProcessor processor = new MyExtractMethodProcessor(project, editor, elements); + try { + processor.prepare(); + processor.showDialog(); + + final PsiMethod emptyMethod = processor.generateEmptyMethod("name"); + final Collection types = FunctionalInterfaceSuggester.suggestFunctionalInterfaces(emptyMethod); + if (types.isEmpty()) { + return false; + } + + if (types.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { + final PsiType next = types.iterator().next(); + functionalInterfaceSelected(next, enclosingMethods, project, editor, processor); + } + else { + final Map classes = new LinkedHashMap(); + for (PsiType type : types) { + classes.put(PsiUtil.resolveClassInType(type), type); + } + final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]); + NavigationUtil.getPsiElementPopup(psiClasses, new PsiClassListCellRenderer(), "Choose From Applicable Functional Interfaces", + new PsiElementProcessor() { + @Override + public boolean execute(@NotNull PsiClass psiClass) { + functionalInterfaceSelected(classes.get(psiClass), enclosingMethods, project, editor, processor); + return true; + } + }).showInBestPositionFor(editor); + return true; + } + + return true; + } + catch (IncorrectOperationException ignore) {} + catch (PrepareFailedException ignore) {} + } + } + return false; + } + + private void functionalInterfaceSelected(final PsiType selectedType, + final List enclosingMethods, + final Project project, + final Editor editor, + final MyExtractMethodProcessor processor) { + final PairConsumer consumer = new PairConsumer() { + @Override + public void consume(PsiMethod methodToIntroduceParameter, PsiMethod methodToSearchFor) { + introduceWrappedCodeBlockParameter(methodToIntroduceParameter, methodToSearchFor, editor, project, selectedType, processor); + } + }; + chooseMethodToIntroduceParameter(editor, enclosingMethods, consumer); + } + + private void introduceWrappedCodeBlockParameter(PsiMethod methodToIntroduceParameter, + PsiMethod methodToSearchFor, Editor editor, + final Project project, + final PsiType selectedType, + final MyExtractMethodProcessor processor) { + final PsiElement[] elements = processor.getElements(); + final PsiElement commonParent = PsiTreeUtil.findCommonParent(elements); + final RangeMarker marker = editor.getDocument().createRangeMarker(elements[0].getTextOffset(), + elements[elements.length - 1].getTextRange().getEndOffset()); + final PsiClass wrapperClass = PsiUtil.resolveClassInType(selectedType); + LOG.assertTrue(wrapperClass != null); + + final Ref methodCallText = new Ref(); + final Ref methodText = new Ref(); + WriteCommandAction.runWriteCommandAction(project, new Runnable() { + @Override + public void run() { + final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(wrapperClass); + LOG.assertTrue(method != null); + final String interfaceMethodName = method.getName(); + processor.setMethodName(interfaceMethodName); + processor.doExtract(); + + final PsiMethod extractedMethod = processor.getExtractedMethod(); + methodText.set(extractedMethod.getText()); + + final PsiMethodCallExpression methodCall = processor.getMethodCall(); + methodCallText.set(methodCall.getText()); + + methodCall.delete(); + extractedMethod.delete(); + } + }); + + final PsiExpression expression = JavaPsiFacade.getElementFactory(project) + .createExpressionFromText("new " + wrapperClass.getQualifiedName() + "() {" + methodText.get() + "}", + methodToIntroduceParameter); + expression.putUserData(ElementToWorkOn.PARENT, commonParent); + expression.putUserData(ElementToWorkOn.SUFFIX, "." + methodCallText.get() + ";"); + + expression.putUserData(ElementToWorkOn.TEXT_RANGE, marker); + new Introducer(project, expression, null, editor) + .introduceParameter(methodToIntroduceParameter, methodToSearchFor); + } + + private static class MyExtractMethodProcessor extends ExtractMethodProcessor { + public MyExtractMethodProcessor(Project project, Editor editor, PsiElement[] elements) { + super(project, editor, elements, null, REFACTORING_NAME, null, null); + } + + @Override + protected AbstractExtractDialog createExtractMethodDialog(boolean direct) { + return new MyAbstractExtractDialog(); + } + + public void setMethodName(String methodName) { + myMethodName = methodName; + } + + @Override + public Boolean hasDuplicates() { + return false; + } + + @Override + protected void deleteExtracted() throws IncorrectOperationException {} + + private class MyAbstractExtractDialog implements AbstractExtractDialog { + @Override + public String getChosenMethodName() { + return "name"; + } + + @Override + public VariableData[] getChosenParameters() { + final InputVariables inputVariables = getInputVariables(); + return inputVariables.getInputVariables().toArray(new VariableData[inputVariables.getInputVariables().size()]); + } + + @Override + public String getVisibility() { + return PsiModifier.PUBLIC; + } + + @Override + public boolean isMakeStatic() { + return false; + } + + @Override + public boolean isChainedConstructor() { + return false; + } + + @Override + public PsiType getReturnType() { + return null; + } + + @Override + public void show() {} + + @Override + public boolean isOK() { + return true; + } + } + } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 5b333dc962af..c27d3f9c23f7 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -432,11 +432,11 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final String fakeInitializer = "intellijidearulezzz"; final int[] refIdx = new int[1]; - final PsiExpression toBeExpression = createReplacement(fakeInitializer, project, prefix, suffix, parent, rangeMarker, refIdx); + final PsiElement toBeExpression = createReplacement(fakeInitializer, project, prefix, suffix, parent, rangeMarker, refIdx); toBeExpression.accept(errorsVisitor); if (hasErrors[0]) return null; - if (literalExpression != null) { - PsiType type = toBeExpression.getType(); + if (literalExpression != null && toBeExpression instanceof PsiExpression) { + PsiType type = ((PsiExpression)toBeExpression).getType(); if (type != null && !type.equals(literalExpression.getType())) { return null; } @@ -1014,7 +1014,7 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { } } - private static PsiExpression createReplacement(final String refText, final Project project, + private static PsiElement createReplacement(final String refText, final Project project, final String prefix, final String suffix, final PsiElement parent, final RangeMarker rangeMarker, int[] refIdx) { @@ -1035,7 +1035,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { refIdx[0] = start.length(); text = start + refText + (suffix != null ? suffix : "") + end; } - return JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(text, parent); + final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + return parent instanceof PsiStatement || parent instanceof PsiCodeBlock ? factory.createStatementFromText(text, parent) : factory.createExpressionFromText(text, parent); } private boolean parentStatementNotFound(final Project project, Editor editor) { diff --git a/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterIntConsumer.java b/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterIntConsumer.java new file mode 100644 index 000000000000..2712d88e4127 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterIntConsumer.java @@ -0,0 +1,18 @@ +import java.util.function.IntConsumer; + +class Test { + void bar() { + foo(1, new java.util.function.IntConsumer() { + public void accept(int i) { + System.out.println(i); + System.out.println(i); + } + }); + } + + void foo(int i, IntConsumer anObject){ + + anObject.accept(i); + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterSampleRunnable.java b/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterSampleRunnable.java new file mode 100644 index 000000000000..609a8ce6f337 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalParameter/afterSampleRunnable.java @@ -0,0 +1,16 @@ +class Test { + void bar() { + foo(new Runnable() { + public void run() { + System.out.println(""); + System.out.println(""); + } + }); + } + + void foo(Runnable anObject){ + + anObject.run(); + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeIntConsumer.java b/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeIntConsumer.java new file mode 100644 index 000000000000..565f4be27aaa --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeIntConsumer.java @@ -0,0 +1,12 @@ +class Test { + void bar() { + foo(1); + } + + void foo(int i) { + + System.out.println(i); + System.out.println(i); + + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeSampleRunnable.java b/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeSampleRunnable.java new file mode 100644 index 000000000000..ffc4ef25205a --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalParameter/beforeSampleRunnable.java @@ -0,0 +1,12 @@ +class Test { + void bar() { + foo(); + } + + void foo() { + + System.out.println(""); + System.out.println(""); + + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java new file mode 100644 index 000000000000..834ef9bb8c0c --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceFunctionalParameterTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 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; + +import com.intellij.JavaTestUtil; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.annotations.NotNull; + +@TestDataPath("$CONTENT_ROOT/testData") +public class IntroduceFunctionalParameterTest extends LightRefactoringTestCase { + public void testSampleRunnable() throws Exception { + doTest(null); + } + + public void testIntConsumer() throws Exception { + doTest(null); + } + + @NotNull + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath(); + } + + @Override + protected LanguageLevel getLanguageLevel() { + return LanguageLevel.JDK_1_8; + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk18(); + } + + private void doTest(String conflict) { + boolean enabled = true; + try { + configureByFile("/refactoring/introduceFunctionalParameter/before" + getTestName(false) + ".java"); + enabled = myEditor.getSettings().isVariableInplaceRenameEnabled(); + myEditor.getSettings().setVariableInplaceRenameEnabled(false); + new IntroduceParameterHandler().introduceStrategy(getProject(), getEditor(), getFile()); + checkResultByFile("/refactoring/introduceFunctionalParameter/after" + getTestName(false) + ".java"); + if (conflict != null) { + fail("Conflict expected"); + } + } + catch (BaseRefactoringProcessor.ConflictsInTestsException e) { + if (conflict == null) { + throw e; + } + assertEquals(conflict, e.getMessage()); + } finally { + myEditor.getSettings().setVariableInplaceRenameEnabled(enabled); + } + } + + +}