a82b31f8615c559fedb7500cd29105d08c9583ba
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / extractMethod / ExtractMethodProcessor.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.refactoring.extractMethod;
17
18 import com.intellij.codeInsight.ChangeContextUtil;
19 import com.intellij.codeInsight.CodeInsightUtil;
20 import com.intellij.codeInsight.ExceptionUtil;
21 import com.intellij.codeInsight.NullableNotNullManager;
22 import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
23 import com.intellij.codeInsight.daemon.impl.quickfix.AnonymousTargetClassPreselectionUtil;
24 import com.intellij.codeInsight.generation.GenerateMembersUtil;
25 import com.intellij.codeInsight.highlighting.HighlightManager;
26 import com.intellij.codeInsight.intention.impl.AddNullableNotNullAnnotationFix;
27 import com.intellij.codeInsight.navigation.NavigationUtil;
28 import com.intellij.codeInspection.dataFlow.*;
29 import com.intellij.codeInspection.dataFlow.instructions.BranchingInstruction;
30 import com.intellij.codeInspection.dataFlow.instructions.Instruction;
31 import com.intellij.ide.DataManager;
32 import com.intellij.ide.util.PropertiesComponent;
33 import com.intellij.ide.util.PsiClassListCellRenderer;
34 import com.intellij.openapi.application.ApplicationManager;
35 import com.intellij.openapi.diagnostic.Logger;
36 import com.intellij.openapi.editor.Editor;
37 import com.intellij.openapi.editor.LogicalPosition;
38 import com.intellij.openapi.editor.ScrollType;
39 import com.intellij.openapi.editor.colors.EditorColors;
40 import com.intellij.openapi.editor.colors.EditorColorsManager;
41 import com.intellij.openapi.editor.markup.TextAttributes;
42 import com.intellij.openapi.progress.ProgressManager;
43 import com.intellij.openapi.project.Project;
44 import com.intellij.openapi.util.Comparing;
45 import com.intellij.openapi.util.Pair;
46 import com.intellij.openapi.util.Pass;
47 import com.intellij.openapi.util.TextRange;
48 import com.intellij.openapi.util.text.StringUtil;
49 import com.intellij.openapi.vfs.VirtualFile;
50 import com.intellij.openapi.wm.WindowManager;
51 import com.intellij.psi.*;
52 import com.intellij.psi.codeStyle.*;
53 import com.intellij.psi.controlFlow.*;
54 import com.intellij.psi.controlFlow.ControlFlow;
55 import com.intellij.psi.impl.source.codeStyle.JavaCodeStyleManagerImpl;
56 import com.intellij.psi.scope.processor.VariablesProcessor;
57 import com.intellij.psi.scope.util.PsiScopesUtil;
58 import com.intellij.psi.search.GlobalSearchScope;
59 import com.intellij.psi.search.LocalSearchScope;
60 import com.intellij.psi.search.PsiElementProcessor;
61 import com.intellij.psi.search.SearchScope;
62 import com.intellij.psi.search.searches.ReferencesSearch;
63 import com.intellij.psi.tree.IElementType;
64 import com.intellij.psi.util.*;
65 import com.intellij.refactoring.HelpID;
66 import com.intellij.refactoring.RefactoringBundle;
67 import com.intellij.refactoring.extractMethodObject.ExtractMethodObjectHandler;
68 import com.intellij.refactoring.introduceField.ElementToWorkOn;
69 import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
70 import com.intellij.refactoring.util.*;
71 import com.intellij.refactoring.util.classMembers.ElementNeedsThis;
72 import com.intellij.refactoring.util.duplicates.*;
73 import com.intellij.util.ArrayUtil;
74 import com.intellij.util.IncorrectOperationException;
75 import com.intellij.util.VisibilityUtil;
76 import com.intellij.util.containers.ContainerUtil;
77 import com.intellij.util.containers.MultiMap;
78 import org.jetbrains.annotations.NonNls;
79 import org.jetbrains.annotations.NotNull;
80 import org.jetbrains.annotations.Nullable;
81 import org.jetbrains.annotations.TestOnly;
82
83 import java.util.*;
84
85 public class ExtractMethodProcessor implements MatchProvider {
86   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractMethod.ExtractMethodProcessor");
87
88   protected final Project myProject;
89   private final Editor myEditor;
90   protected final PsiElement[] myElements;
91   private final PsiBlockStatement myEnclosingBlockStatement;
92   private final PsiType myForcedReturnType;
93   private final String myRefactoringName;
94   protected final String myInitialMethodName;
95   private final String myHelpId;
96
97   private final PsiManager myManager;
98   private final PsiElementFactory myElementFactory;
99   private final CodeStyleManager myStyleManager;
100
101   private PsiExpression myExpression;
102
103   private PsiElement myCodeFragmentMember; // parent of myCodeFragment
104
105   protected String myMethodName; // name for extracted method
106   protected PsiType myReturnType; // return type for extracted method
107   protected PsiTypeParameterList myTypeParameterList; //type parameter list of extracted method
108   private VariableData[] myVariableDatum; // parameter data for extracted method
109   protected PsiClassType[] myThrownExceptions; // exception to declare as thrown by extracted method
110   protected boolean myStatic; // whether to declare extracted method static
111
112   protected PsiClass myTargetClass; // class to create the extracted method in
113   private PsiElement myAnchor; // anchor to insert extracted method after it
114
115   protected ControlFlowWrapper myControlFlowWrapper;
116   protected InputVariables myInputVariables; // input variables
117   protected PsiVariable[] myOutputVariables; // output variables
118   protected PsiVariable myOutputVariable; // the only output variable
119   private   PsiVariable myArtificialOutputVariable;
120   private Collection<PsiStatement> myExitStatements;
121
122   private boolean myHasReturnStatement; // there is a return statement
123   private boolean myHasReturnStatementOutput; // there is a return statement and its type is not void
124   protected boolean myHasExpressionOutput; // extracted code is an expression with non-void type
125   private boolean myNeedChangeContext; // target class is not immediate container of the code to be extracted
126
127   private boolean myShowErrorDialogs = true;
128   protected boolean myCanBeStatic;
129   protected boolean myCanBeChainedConstructor;
130   protected boolean myIsChainedConstructor;
131   private List<Match> myDuplicates;
132   @PsiModifier.ModifierConstant private String myMethodVisibility = PsiModifier.PRIVATE;
133   protected boolean myGenerateConditionalExit;
134   protected PsiStatement myFirstExitStatementCopy;
135   private PsiMethod myExtractedMethod;
136   private PsiMethodCallExpression myMethodCall;
137   protected boolean myNullConditionalCheck;
138   protected boolean myNotNullConditionalCheck;
139   private Nullness myNullness;
140
141   public ExtractMethodProcessor(Project project,
142                                 Editor editor,
143                                 PsiElement[] elements,
144                                 PsiType forcedReturnType,
145                                 String refactoringName,
146                                 String initialMethodName,
147                                 String helpId) {
148     myProject = project;
149     myEditor = editor;
150     if (elements.length != 1 || !(elements[0] instanceof PsiBlockStatement)) {
151       myElements = elements.length == 1 && elements[0] instanceof PsiParenthesizedExpression
152                    ? new PsiElement[] {PsiUtil.skipParenthesizedExprDown((PsiExpression)elements[0])} : elements;
153       myEnclosingBlockStatement = null;
154     }
155     else {
156       myEnclosingBlockStatement = (PsiBlockStatement)elements[0];
157       PsiElement[] codeBlockChildren = myEnclosingBlockStatement.getCodeBlock().getChildren();
158       myElements = processCodeBlockChildren(codeBlockChildren);
159     }
160     myForcedReturnType = forcedReturnType;
161     myRefactoringName = refactoringName;
162     myInitialMethodName = initialMethodName;
163     myHelpId = helpId;
164
165     myManager = PsiManager.getInstance(myProject);
166     myElementFactory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
167     myStyleManager = CodeStyleManager.getInstance(myProject);
168   }
169
170   private static PsiElement[] processCodeBlockChildren(PsiElement[] codeBlockChildren) {
171     int resultLast = codeBlockChildren.length;
172
173     if (codeBlockChildren.length == 0) return PsiElement.EMPTY_ARRAY;
174
175     final PsiElement first = codeBlockChildren[0];
176     int resultStart = 0;
177     if (first instanceof PsiJavaToken && ((PsiJavaToken)first).getTokenType() == JavaTokenType.LBRACE) {
178       resultStart++;
179     }
180     final PsiElement last = codeBlockChildren[codeBlockChildren.length - 1];
181     if (last instanceof PsiJavaToken && ((PsiJavaToken)last).getTokenType() == JavaTokenType.RBRACE) {
182       resultLast--;
183     }
184     final ArrayList<PsiElement> result = new ArrayList<>();
185     for (int i = resultStart; i < resultLast; i++) {
186       PsiElement element = codeBlockChildren[i];
187       if (!(element instanceof PsiWhiteSpace)) {
188         result.add(element);
189       }
190     }
191
192     return PsiUtilCore.toPsiElementArray(result);
193   }
194
195   /**
196    * Method for test purposes
197    */
198   public void setShowErrorDialogs(boolean showErrorDialogs) {
199     myShowErrorDialogs = showErrorDialogs;
200   }
201
202   public void setChainedConstructor(final boolean isChainedConstructor) {
203     myIsChainedConstructor = isChainedConstructor;
204   }
205
206
207   public boolean prepare() throws PrepareFailedException {
208     return prepare(null);
209   }
210
211   /**
212    * Invoked in atomic action
213    */
214   public boolean prepare(@Nullable Pass<ExtractMethodProcessor> pass) throws PrepareFailedException {
215     myExpression = null;
216     if (myElements.length == 1 && myElements[0] instanceof PsiExpression) {
217       final PsiExpression expression = (PsiExpression)myElements[0];
218       if (expression instanceof PsiAssignmentExpression && expression.getParent() instanceof PsiExpressionStatement) {
219         myElements[0] = expression.getParent();
220       }
221       else {
222         myExpression = expression;
223       }
224     }
225
226     final PsiElement codeFragment = ControlFlowUtil.findCodeFragment(myElements[0]);
227     myCodeFragmentMember = codeFragment.getUserData(ElementToWorkOn.PARENT);
228     if (myCodeFragmentMember == null) {
229       myCodeFragmentMember = codeFragment.getParent();
230     }
231     if (myCodeFragmentMember == null) {
232       myCodeFragmentMember = ControlFlowUtil.findCodeFragment(codeFragment.getContext()).getParent();
233     }
234
235     myControlFlowWrapper = new ControlFlowWrapper(myProject, codeFragment, myElements);
236
237     try {
238       myExitStatements = myControlFlowWrapper.prepareExitStatements(myElements, codeFragment);
239       if (myControlFlowWrapper.isGenerateConditionalExit()) {
240         myGenerateConditionalExit = true;
241       } else {
242         myHasReturnStatement = myExpression == null && myControlFlowWrapper.isReturnPresentBetween();
243       }
244       myFirstExitStatementCopy = myControlFlowWrapper.getFirstExitStatementCopy();
245     }
246     catch (ControlFlowWrapper.ExitStatementsNotSameException e) {
247       myExitStatements = myControlFlowWrapper.getExitStatements();
248       myNotNullConditionalCheck = areAllExitPointsAreNotNull(getExpectedReturnType());
249       if (!myNotNullConditionalCheck) {
250         showMultipleExitPointsMessage();
251         return false;
252       }
253     }
254
255     myOutputVariables = myControlFlowWrapper.getOutputVariables();
256
257     return chooseTargetClass(codeFragment, pass);
258   }
259
260   private boolean checkExitPoints() throws PrepareFailedException {
261     PsiType expressionType = null;
262     if (myExpression != null) {
263       if (myForcedReturnType != null) {
264         expressionType = myForcedReturnType;
265       }
266       else {
267         expressionType = RefactoringUtil.getTypeByExpressionWithExpectedType(myExpression);
268         if (expressionType == null && !(myExpression.getParent() instanceof PsiExpressionStatement)) {
269           expressionType = PsiType.getJavaLangObject(myExpression.getManager(), GlobalSearchScope.allScope(myProject));
270         }
271       }
272     }
273     if (expressionType == null) {
274       expressionType = PsiType.VOID;
275     }
276     myHasExpressionOutput = !PsiType.VOID.equals(expressionType);
277
278     final PsiType returnStatementType = getExpectedReturnType();
279     myHasReturnStatementOutput = myHasReturnStatement && returnStatementType != null && !PsiType.VOID.equals(returnStatementType);
280
281     if (myGenerateConditionalExit && myOutputVariables.length == 1) {
282       if (!(myOutputVariables[0].getType() instanceof PsiPrimitiveType)) {
283         myNullConditionalCheck = true;
284         for (PsiStatement exitStatement : myExitStatements) {
285           if (exitStatement instanceof PsiReturnStatement) {
286             final PsiExpression returnValue = ((PsiReturnStatement)exitStatement).getReturnValue();
287             myNullConditionalCheck &= returnValue == null || isNullInferred(returnValue.getText(), true);
288           }
289         }
290         myNullConditionalCheck &= isNullInferred(myOutputVariables[0].getName(), false);
291       }
292
293       myNotNullConditionalCheck = areAllExitPointsAreNotNull(returnStatementType);
294     }
295
296     if (!myHasReturnStatementOutput && checkOutputVariablesCount() && !myNullConditionalCheck && !myNotNullConditionalCheck) {
297       showMultipleOutputMessage(expressionType);
298       return false;
299     }
300
301     myOutputVariable = myOutputVariables.length > 0 ? myOutputVariables[0] : null;
302     if (myNotNullConditionalCheck) {
303       myReturnType = returnStatementType instanceof PsiPrimitiveType ? ((PsiPrimitiveType)returnStatementType).getBoxedType(myCodeFragmentMember) 
304                                                                      : returnStatementType;
305     } else if (myHasReturnStatementOutput) {
306       myReturnType = returnStatementType;
307     }
308     else if (myOutputVariable != null) {
309       myReturnType = myOutputVariable.getType();
310     }
311     else if (myGenerateConditionalExit) {
312       myReturnType = PsiType.BOOLEAN;
313     }
314     else {
315       myReturnType = expressionType;
316     }
317
318     PsiElement container = PsiTreeUtil.getParentOfType(myElements[0], PsiClass.class, PsiMethod.class);
319     while (container instanceof PsiMethod && ((PsiMethod)container).getContainingClass() != myTargetClass) {
320       container = PsiTreeUtil.getParentOfType(container, PsiMethod.class, true);
321     }
322     if (container instanceof PsiMethod) {
323       PsiElement[] elements = myElements;
324       if (myExpression == null) {
325         if (myOutputVariable != null) {
326           elements = ArrayUtil.append(myElements, myOutputVariable, PsiElement.class);
327         }
328         if (myCodeFragmentMember instanceof PsiMethod && myReturnType == ((PsiMethod)myCodeFragmentMember).getReturnType()) {
329           elements = ArrayUtil.append(myElements, ((PsiMethod)myCodeFragmentMember).getReturnTypeElement(), PsiElement.class);
330         }
331       }
332       myTypeParameterList = RefactoringUtil.createTypeParameterListWithUsedTypeParameters(((PsiMethod)container).getTypeParameterList(),
333                                                                                           elements);
334     }
335     List<PsiClassType> exceptions = ExceptionUtil.getThrownCheckedExceptions(myElements);
336     myThrownExceptions = exceptions.toArray(new PsiClassType[exceptions.size()]);
337
338     if (container instanceof PsiMethod) {
339       checkLocalClasses((PsiMethod) container);
340     }
341     return true;
342   }
343
344   private PsiType getExpectedReturnType() {
345     return myCodeFragmentMember instanceof PsiMethod 
346                                         ? ((PsiMethod)myCodeFragmentMember).getReturnType()
347                                         : myCodeFragmentMember instanceof PsiLambdaExpression 
348                                           ? LambdaUtil.getFunctionalInterfaceReturnType((PsiLambdaExpression)myCodeFragmentMember) 
349                                           : null;
350   }
351
352   @Nullable
353   private PsiVariable getArtificialOutputVariable() {
354     if (myOutputVariables.length == 0 && myExitStatements.isEmpty()) {
355       if (myCanBeChainedConstructor) {
356         final Set<PsiField> fields = new HashSet<>();
357         for (PsiElement element : myElements) {
358           element.accept(new JavaRecursiveElementWalkingVisitor() {
359             @Override
360             public void visitReferenceExpression(PsiReferenceExpression expression) {
361               super.visitReferenceExpression(expression);
362               final PsiElement resolve = expression.resolve();
363               if (resolve instanceof PsiField && ((PsiField)resolve).hasModifierProperty(PsiModifier.FINAL) &&
364                   PsiUtil.isAccessedForWriting(expression)) {
365                 fields.add((PsiField)resolve);
366               }
367             }
368           });
369         }
370         if (!fields.isEmpty()) {
371           return fields.size() == 1 ? fields.iterator().next() : null;
372         }
373       }
374       final VariablesProcessor processor = new VariablesProcessor(true) {
375         @Override
376         protected boolean check(PsiVariable var, ResolveState state) {
377           return isDeclaredInside(var);
378         }
379       };
380       PsiScopesUtil.treeWalkUp(processor, myElements[myElements.length - 1], myCodeFragmentMember);
381       if (processor.size() == 1) {
382         return processor.getResult(0);
383       }
384     }
385     return null;
386   }
387
388   private boolean areAllExitPointsAreNotNull(PsiType returnStatementType) {
389     if (insertNotNullCheckIfPossible() && myControlFlowWrapper.getOutputVariables(false).length == 0) {
390       boolean isNotNull = returnStatementType != null && !PsiType.VOID.equals(returnStatementType);
391       for (PsiStatement statement : myExitStatements) {
392         if (statement instanceof PsiReturnStatement) {
393           final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue();
394           isNotNull &= returnValue != null && !isNullInferred(returnValue.getText(), true);
395         }
396       }
397       return isNotNull;
398     }
399     return false;
400   }
401
402   protected boolean insertNotNullCheckIfPossible() {
403     return true;
404   }
405
406   private boolean isNullInferred(String exprText, boolean trueSet) {
407     final PsiCodeBlock block = myElementFactory.createCodeBlockFromText("{}", myElements[0]);
408     for (PsiElement element : myElements) {
409       block.add(element);
410     }
411     final PsiIfStatement statementFromText = (PsiIfStatement)myElementFactory.createStatementFromText("if (" + exprText + " == null);", null);
412     block.add(statementFromText);
413
414     final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner();
415     final StandardInstructionVisitor visitor = new StandardInstructionVisitor();
416     final RunnerResult rc = dfaRunner.analyzeMethod(block, visitor);
417     if (rc == RunnerResult.OK) {
418       final Pair<Set<Instruction>, Set<Instruction>> expressions = dfaRunner.getConstConditionalExpressions();
419       final Set<Instruction> set = trueSet ? expressions.getFirst() : expressions.getSecond();
420       for (Instruction instruction : set) {
421         if (instruction instanceof BranchingInstruction) {
422           if (((BranchingInstruction)instruction).getPsiAnchor().getText().equals(statementFromText.getCondition().getText())) {
423             return true;
424           }
425         }
426       }
427     }
428     return false;
429   }
430
431   protected boolean checkOutputVariablesCount() {
432     int outputCount = (myHasExpressionOutput ? 1 : 0) + (myGenerateConditionalExit ? 1 : 0) + myOutputVariables.length;
433     return outputCount > 1;
434   }
435
436   private void checkCanBeChainedConstructor() {
437     if (!(myCodeFragmentMember instanceof PsiMethod)) {
438       return;
439     }
440     final PsiMethod method = (PsiMethod)myCodeFragmentMember;
441     if (!method.isConstructor() || !PsiType.VOID.equals(myReturnType)) {
442       return;
443     }
444     final PsiCodeBlock body = method.getBody();
445     if (body == null) return;
446     final PsiStatement[] psiStatements = body.getStatements();
447     if (psiStatements.length > 0 && myElements [0] == psiStatements [0]) {
448       myCanBeChainedConstructor = true;
449     }
450   }
451
452   private void checkLocalClasses(final PsiMethod container) throws PrepareFailedException {
453     final List<PsiClass> localClasses = new ArrayList<>();
454     container.accept(new JavaRecursiveElementWalkingVisitor() {
455       @Override public void visitClass(final PsiClass aClass) {
456         localClasses.add(aClass);
457       }
458
459       @Override public void visitAnonymousClass(final PsiAnonymousClass aClass) {
460         visitElement(aClass);
461       }
462
463       @Override public void visitTypeParameter(final PsiTypeParameter classParameter) {
464         visitElement(classParameter);
465       }
466     });
467     for(PsiClass localClass: localClasses) {
468       final boolean classExtracted = isExtractedElement(localClass);
469       final List<PsiElement> extractedReferences = Collections.synchronizedList(new ArrayList<PsiElement>());
470       final List<PsiElement> remainingReferences = Collections.synchronizedList(new ArrayList<PsiElement>());
471       ReferencesSearch.search(localClass).forEach(psiReference -> {
472         final PsiElement element = psiReference.getElement();
473         final boolean elementExtracted = isExtractedElement(element);
474         if (elementExtracted && !classExtracted) {
475           extractedReferences.add(element);
476           return false;
477         }
478         if (!elementExtracted && classExtracted) {
479           remainingReferences.add(element);
480           return false;
481         }
482         return true;
483       });
484       if (!extractedReferences.isEmpty()) {
485         throw new PrepareFailedException("Cannot extract method because the selected code fragment uses local classes defined outside of the fragment", extractedReferences.get(0));
486       }
487       if (!remainingReferences.isEmpty()) {
488         throw new PrepareFailedException("Cannot extract method because the selected code fragment defines local classes used outside of the fragment", remainingReferences.get(0));
489       }
490       if (classExtracted) {
491         for (PsiVariable variable : myControlFlowWrapper.getUsedVariables()) {
492           if (isDeclaredInside(variable) && !variable.equals(myOutputVariable) && PsiUtil.resolveClassInType(variable.getType()) == localClass) {
493             throw new PrepareFailedException("Cannot extract method because the selected code fragment defines variable of local class type used outside of the fragment", variable);
494           }
495         }
496       }
497     }
498   }
499
500   private boolean isExtractedElement(final PsiElement element) {
501     boolean isExtracted = false;
502     for(PsiElement psiElement: myElements) {
503       if (PsiTreeUtil.isAncestor(psiElement, element, false)) {
504         isExtracted = true;
505         break;
506       }
507     }
508     return isExtracted;
509   }
510
511
512
513   private boolean shouldBeStatic() {
514     for(PsiElement element: myElements) {
515       final PsiExpressionStatement statement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class);
516       if (statement != null && JavaHighlightUtil.isSuperOrThisCall(statement, true, true)) {
517         return true;
518       }
519     }
520     PsiElement codeFragmentMember = myCodeFragmentMember;
521     while (codeFragmentMember != null && PsiTreeUtil.isAncestor(myTargetClass, codeFragmentMember, true)) {
522       if (codeFragmentMember instanceof PsiModifierListOwner && ((PsiModifierListOwner)codeFragmentMember).hasModifierProperty(PsiModifier.STATIC)) {
523         return true;
524       }
525       codeFragmentMember = PsiTreeUtil.getParentOfType(codeFragmentMember, PsiModifierListOwner.class, true);
526     }
527     return false;
528   }
529
530   public boolean showDialog(final boolean direct) {
531     AbstractExtractDialog dialog = createExtractMethodDialog(direct);
532     dialog.show();
533     if (!dialog.isOK()) return false;
534     apply(dialog);
535     return true;
536   }
537
538   protected void apply(final AbstractExtractDialog dialog) {
539     myMethodName = dialog.getChosenMethodName();
540     myVariableDatum = dialog.getChosenParameters();
541     myStatic = isStatic() | dialog.isMakeStatic();
542     myIsChainedConstructor = dialog.isChainedConstructor();
543     myMethodVisibility = dialog.getVisibility();
544
545     final PsiType returnType = dialog.getReturnType();
546     if (returnType != null) {
547       myReturnType = returnType;
548     }
549   }
550
551   protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) {
552     final List<VariableData> variables = myInputVariables.getInputVariables();
553     myVariableDatum = variables.toArray(new VariableData[variables.size()]);
554     myNullness = initNullness();
555     myArtificialOutputVariable = PsiType.VOID.equals(myReturnType) ? getArtificialOutputVariable() : null;
556     final PsiType returnType = myArtificialOutputVariable != null ? myArtificialOutputVariable.getType() : myReturnType;
557     return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, returnType, getTypeParameterList(),
558                                    getThrownExceptions(), isStatic(), isCanBeStatic(), myCanBeChainedConstructor,
559                                                          myRefactoringName, myHelpId, myNullness, myElements) {
560       protected boolean areTypesDirected() {
561         return direct;
562       }
563
564       @Override
565       protected String[] suggestMethodNames() {
566         return suggestInitialMethodName();
567       }
568
569       @Override
570       protected PsiExpression[] findOccurrences() {
571         return ExtractMethodProcessor.this.findOccurrences();
572       }
573
574       @Override
575       protected boolean isOutputVariable(PsiVariable var) {
576         return ExtractMethodProcessor.this.isOutputVariable(var);
577       }
578
579       protected boolean isVoidReturn() {
580         return myArtificialOutputVariable != null && !(myArtificialOutputVariable instanceof PsiField);
581       }
582
583       @Override
584       protected void checkMethodConflicts(MultiMap<PsiElement, String> conflicts) {
585         super.checkMethodConflicts(conflicts);
586         final VariableData[] parameters = getChosenParameters();
587         final Map<String, PsiLocalVariable> vars = new HashMap<>();
588         for (PsiElement element : myElements) {
589           element.accept(new JavaRecursiveElementWalkingVisitor() {
590             @Override
591             public void visitLocalVariable(PsiLocalVariable variable) {
592               super.visitLocalVariable(variable);
593               vars.put(variable.getName(), variable);
594             }
595
596             @Override
597             public void visitClass(PsiClass aClass) {}
598           });
599         }
600         for (VariableData parameter : parameters) {
601           final String paramName = parameter.name;
602           final PsiLocalVariable variable = vars.get(paramName);
603           if (variable != null) {
604             conflicts.putValue(variable, "Variable with name " + paramName + " is already defined in the selected scope");
605           }
606         }
607       }
608     };
609   }
610
611   public PsiExpression[] findOccurrences() {
612     if (myExpression != null) {
613       return new PsiExpression[] {myExpression};
614     }
615     if (myOutputVariable != null) {
616       final PsiElement scope = myOutputVariable instanceof PsiLocalVariable 
617                                ? RefactoringUtil.getVariableScope((PsiLocalVariable)myOutputVariable) 
618                                : PsiTreeUtil.findCommonParent(myElements);
619       return CodeInsightUtil.findReferenceExpressions(scope, myOutputVariable);
620     }
621     final List<PsiStatement> filter = ContainerUtil.filter(myExitStatements, statement -> statement instanceof PsiReturnStatement && ((PsiReturnStatement)statement).getReturnValue() != null);
622     final List<PsiExpression> map = ContainerUtil.map(filter, statement -> ((PsiReturnStatement)statement).getReturnValue());
623     return map.toArray(new PsiExpression[map.size()]);
624   }
625
626   private Nullness initNullness() {
627     if (!PsiUtil.isLanguageLevel5OrHigher(myElements[0]) || PsiUtil.resolveClassInType(myReturnType) == null) return null;
628     final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject);
629     final PsiClass nullableAnnotationClass = JavaPsiFacade.getInstance(myProject)
630       .findClass(manager.getDefaultNullable(), myElements[0].getResolveScope());
631     if (nullableAnnotationClass != null) {
632       final PsiElement elementInCopy = myTargetClass.getContainingFile().copy().findElementAt(myTargetClass.getTextOffset());
633       final PsiClass classCopy = PsiTreeUtil.getParentOfType(elementInCopy, PsiClass.class);
634       if (classCopy == null) {
635         return null;
636       }
637       final PsiMethod emptyMethod = (PsiMethod)classCopy.addAfter(generateEmptyMethod("name"), classCopy.getLBrace());
638       prepareMethodBody(emptyMethod, false);
639       if (myNotNullConditionalCheck || myNullConditionalCheck) {
640         return Nullness.NULLABLE;
641       }
642       return DfaUtil.inferMethodNullity(emptyMethod);
643     }
644     return null;
645   }
646
647   protected String[] suggestInitialMethodName() {
648     if (StringUtil.isEmpty(myInitialMethodName)) {
649       final Set<String> initialMethodNames = new LinkedHashSet<>();
650       final JavaCodeStyleManagerImpl codeStyleManager = (JavaCodeStyleManagerImpl)JavaCodeStyleManager.getInstance(myProject);
651       if (myExpression != null || !(myReturnType instanceof PsiPrimitiveType)) {
652         final String[] names = codeStyleManager.suggestVariableName(VariableKind.FIELD, null, myExpression, myReturnType).names;
653         for (String name : names) {
654           initialMethodNames.add(codeStyleManager.variableNameToPropertyName(name, VariableKind.FIELD));
655         }
656       }
657
658       if (myOutputVariable != null) {
659         final VariableKind outKind = codeStyleManager.getVariableKind(myOutputVariable);
660         final SuggestedNameInfo nameInfo = codeStyleManager
661           .suggestVariableName(VariableKind.FIELD, codeStyleManager.variableNameToPropertyName(myOutputVariable.getName(), outKind), null, myOutputVariable.getType());
662         for (String name : nameInfo.names) {
663           initialMethodNames.add(codeStyleManager.variableNameToPropertyName(name, VariableKind.FIELD));
664         }
665       }
666
667       final String nameByComment = getNameByComment();
668       final PsiField field = JavaPsiFacade.getElementFactory(myProject).createField("fieldNameToReplace", myReturnType instanceof PsiEllipsisType ? ((PsiEllipsisType)myReturnType).toArrayType() : myReturnType);
669       final List<String> getters = new ArrayList<>(ContainerUtil.map(initialMethodNames, propertyName -> {
670         if (!PsiNameHelper.getInstance(myProject).isIdentifier(propertyName)) {
671           LOG.info(propertyName + "; " + myExpression);
672           return null;
673         }
674         field.setName(propertyName);
675         return GenerateMembersUtil.suggestGetterName(field);
676       }));
677       ContainerUtil.addIfNotNull(getters, nameByComment);
678       return ArrayUtil.toStringArray(getters);
679     }
680     return new String[] {myInitialMethodName};
681   }
682
683   private String getNameByComment() {
684     PsiElement prevSibling = PsiTreeUtil.skipSiblingsBackward(myElements[0], PsiWhiteSpace.class);
685     if (prevSibling instanceof PsiComment && ((PsiComment)prevSibling).getTokenType() == JavaTokenType.END_OF_LINE_COMMENT) {
686       final String text = StringUtil.decapitalize(StringUtil.capitalizeWords(prevSibling.getText().trim().substring(2), true)).replaceAll(" ", "");
687       if (PsiNameHelper.getInstance(myProject).isIdentifier(text) && text.length() < 20) {
688         return text;
689       }
690     }
691     return null;
692   }
693
694   public boolean isOutputVariable(PsiVariable var) {
695     return ArrayUtil.find(myOutputVariables, var) != -1;
696   }
697
698   public boolean showDialog() {
699     return showDialog(true);
700   }
701
702   @TestOnly
703   public void testRun() throws IncorrectOperationException {
704     testPrepare();
705     testNullness();
706     ExtractMethodHandler.run(myProject, myEditor, this);
707   }
708
709   @TestOnly
710   public void testNullness() {
711     myNullness = initNullness();
712   }
713
714   @TestOnly
715   public void testPrepare() {
716     myInputVariables.setFoldingAvailable(myInputVariables.isFoldingSelectedByDefault());
717     myMethodName = myInitialMethodName;
718     myVariableDatum = new VariableData[myInputVariables.getInputVariables().size()];
719     for (int i = 0; i < myInputVariables.getInputVariables().size(); i++) {
720       myVariableDatum[i] = myInputVariables.getInputVariables().get(i);
721     }
722   }
723
724   @TestOnly
725   public void testTargetClass(PsiClass targetClass) {
726     if (targetClass != null) {
727       myTargetClass = targetClass;
728       myNeedChangeContext = true;
729     }
730   }
731
732   @TestOnly
733   public void testPrepare(PsiType returnType, boolean makeStatic) throws PrepareFailedException{
734     if (makeStatic) {
735       if (!isCanBeStatic()) {
736         throw new PrepareFailedException("Failed to make static", myElements[0]);
737       }
738       myInputVariables.setPassFields(true);
739       myStatic = true;
740     }
741     if (PsiType.VOID.equals(myReturnType)) {
742       myArtificialOutputVariable = getArtificialOutputVariable();
743     }
744     testPrepare();
745     if (returnType != null) {
746       myReturnType = returnType;
747     }
748   }
749
750   @TestOnly
751   public void doNotPassParameter(int i) {
752     myVariableDatum[i].passAsParameter = false;
753   }
754
755   @TestOnly
756   public void changeParamName(int i, String param) {
757     myVariableDatum[i].name = param;
758   }
759
760   /**
761    * Invoked in command and in atomic action
762    */
763   public void doRefactoring() throws IncorrectOperationException {
764     initDuplicates();
765
766     chooseAnchor();
767
768     LogicalPosition pos1;
769     if (myEditor != null) {
770       int col = myEditor.getCaretModel().getLogicalPosition().column;
771       int line = myEditor.getCaretModel().getLogicalPosition().line;
772       pos1 = new LogicalPosition(line, col);
773       LogicalPosition pos = new LogicalPosition(0, 0);
774       myEditor.getCaretModel().moveToLogicalPosition(pos);
775     } else {
776       pos1 = null;
777     }
778
779     final SearchScope processConflictsScope = myMethodVisibility.equals(PsiModifier.PRIVATE) ?
780                                         new LocalSearchScope(myTargetClass) :
781                                         GlobalSearchScope.projectScope(myProject);
782
783     final Map<PsiMethodCallExpression, PsiMethod> overloadsResolveMap = new HashMap<>();
784     final Runnable collectOverloads = () -> ApplicationManager.getApplication().runReadAction(() -> {
785       Map<PsiMethodCallExpression, PsiMethod> overloads =
786         ExtractMethodUtil.encodeOverloadTargets(myTargetClass, processConflictsScope, myMethodName, myCodeFragmentMember);
787       overloadsResolveMap.putAll(overloads);
788     });
789     final Runnable extract = () -> {
790       doExtract();
791       ExtractMethodUtil.decodeOverloadTargets(overloadsResolveMap, myExtractedMethod, myCodeFragmentMember);
792     };
793     if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
794       collectOverloads.run();
795       extract.run();
796     } else {
797       if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(collectOverloads, "Collect overloads...", true, myProject)) return;
798       ApplicationManager.getApplication().runWriteAction(extract);
799     }
800
801     if (myEditor != null) {
802       myEditor.getCaretModel().moveToLogicalPosition(pos1);
803       int offset = myMethodCall.getMethodExpression().getTextRange().getStartOffset();
804       myEditor.getCaretModel().moveToOffset(offset);
805       myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
806       myEditor.getSelectionModel().removeSelection();
807     }
808   }
809
810   @Nullable
811   private DuplicatesFinder initDuplicates() {
812     List<PsiElement> elements = new ArrayList<>();
813     for (PsiElement element : myElements) {
814       if (!(element instanceof PsiWhiteSpace || element instanceof PsiComment)) {
815         elements.add(element);
816       }
817     }
818
819     if (myExpression != null) {
820       DuplicatesFinder finder = new DuplicatesFinder(PsiUtilCore.toPsiElementArray(elements), myInputVariables.copy(),
821                                                      new ArrayList<>());
822       myDuplicates = finder.findDuplicates(myTargetClass);
823       return finder;
824     }
825     else if (elements.size() > 0){
826       DuplicatesFinder myDuplicatesFinder = new DuplicatesFinder(PsiUtilCore.toPsiElementArray(elements), myInputVariables.copy(),
827                                                 myOutputVariable != null ? new VariableReturnValue(myOutputVariable) : null,
828                                                 Arrays.asList(myOutputVariables));
829       myDuplicates = myDuplicatesFinder.findDuplicates(myTargetClass);
830       return myDuplicatesFinder;
831     } else {
832       myDuplicates = new ArrayList<>();
833     }
834     return null;
835   }
836
837   public void doExtract() throws IncorrectOperationException {
838
839     PsiMethod newMethod = generateEmptyMethod();
840
841     myExpression = myInputVariables.replaceWrappedReferences(myElements, myExpression);
842     renameInputVariables();
843
844     LOG.assertTrue(myElements[0].isValid());
845
846     PsiCodeBlock body = newMethod.getBody();
847     myMethodCall = generateMethodCall(null, true);
848
849     LOG.assertTrue(myElements[0].isValid());
850
851     final PsiStatement exitStatementCopy = prepareMethodBody(newMethod, true);
852
853     if (myExpression == null) {
854       if (myNeedChangeContext && isNeedToChangeCallContext()) {
855         for (PsiElement element : myElements) {
856           ChangeContextUtil.encodeContextInfo(element, false);
857         }
858       }
859
860       if (myNullConditionalCheck) {
861         final String varName = myOutputVariable.getName();
862         if (isDeclaredInside(myOutputVariable)) {
863           declareVariableAtMethodCallLocation(varName);
864         }
865         else {
866           PsiExpressionStatement assignmentExpression =
867             (PsiExpressionStatement)myElementFactory.createStatementFromText(varName + "=x;", null);
868           assignmentExpression = (PsiExpressionStatement)addToMethodCallLocation(assignmentExpression);
869           myMethodCall =
870             (PsiMethodCallExpression)((PsiAssignmentExpression)assignmentExpression.getExpression()).getRExpression().replace(myMethodCall);
871         }
872         declareNecessaryVariablesAfterCall(myOutputVariable);
873         PsiIfStatement ifStatement;
874         if (myHasReturnStatementOutput) {
875           ifStatement = (PsiIfStatement)myElementFactory.createStatementFromText("if (" + varName + "==null) return null;", null);
876         }
877         else if (myGenerateConditionalExit) {
878           if (myFirstExitStatementCopy instanceof PsiReturnStatement && ((PsiReturnStatement)myFirstExitStatementCopy).getReturnValue() != null) {
879             ifStatement = (PsiIfStatement)myElementFactory.createStatementFromText("if (" + varName + "==null) return null;", null);
880           } 
881           else {
882             ifStatement = (PsiIfStatement)myElementFactory.createStatementFromText("if (" + varName + "==null) " + myFirstExitStatementCopy.getText(), null);
883           }
884         }
885         else {
886           ifStatement = (PsiIfStatement)myElementFactory.createStatementFromText("if (" + varName + "==null) return;", null);
887         }
888         ifStatement = (PsiIfStatement)addToMethodCallLocation(ifStatement);
889         CodeStyleManager.getInstance(myProject).reformat(ifStatement);
890       }
891       else if (myNotNullConditionalCheck) {
892         String varName = myOutputVariable != null ? myOutputVariable.getName() : "x";
893         varName = declareVariableAtMethodCallLocation(varName, myReturnType instanceof PsiPrimitiveType ? ((PsiPrimitiveType)myReturnType).getBoxedType(myCodeFragmentMember) : myReturnType);
894         addToMethodCallLocation(myElementFactory.createStatementFromText("if (" + varName + " != null) return " + varName + ";", null));
895       }
896       else if (myGenerateConditionalExit) {
897         PsiIfStatement ifStatement = (PsiIfStatement)myElementFactory.createStatementFromText("if (a) b;", null);
898         ifStatement = (PsiIfStatement)addToMethodCallLocation(ifStatement);
899         myMethodCall = (PsiMethodCallExpression)ifStatement.getCondition().replace(myMethodCall);
900         myFirstExitStatementCopy = (PsiStatement)ifStatement.getThenBranch().replace(myFirstExitStatementCopy);
901         CodeStyleManager.getInstance(myProject).reformat(ifStatement);
902       }
903       else if (myOutputVariable != null || isArtificialOutputUsed()) {
904         boolean toDeclare = isArtificialOutputUsed() ? !(myArtificialOutputVariable instanceof PsiField) : isDeclaredInside(myOutputVariable);
905         String name = isArtificialOutputUsed() ? myArtificialOutputVariable.getName() : myOutputVariable.getName();
906         if (!toDeclare) {
907           PsiExpressionStatement statement = (PsiExpressionStatement)myElementFactory.createStatementFromText(name + "=x;", null);
908           statement = (PsiExpressionStatement)myStyleManager.reformat(statement);
909           statement = (PsiExpressionStatement)addToMethodCallLocation(statement);
910           PsiAssignmentExpression assignment = (PsiAssignmentExpression)statement.getExpression();
911           myMethodCall = (PsiMethodCallExpression)assignment.getRExpression().replace(myMethodCall);
912         }
913         else {
914           declareVariableAtMethodCallLocation(name);
915         }
916       }
917       else if (myHasReturnStatementOutput) {
918         PsiStatement statement = myElementFactory.createStatementFromText("return x;", null);
919         statement = (PsiStatement)addToMethodCallLocation(statement);
920         myMethodCall = (PsiMethodCallExpression)((PsiReturnStatement)statement).getReturnValue().replace(myMethodCall);
921       }
922       else {
923         PsiStatement statement = myElementFactory.createStatementFromText("x();", null);
924         statement = (PsiStatement)addToMethodCallLocation(statement);
925         myMethodCall = (PsiMethodCallExpression)((PsiExpressionStatement)statement).getExpression().replace(myMethodCall);
926       }
927       if (myHasReturnStatement && !myHasReturnStatementOutput && !hasNormalExit()) {
928         PsiStatement statement = myElementFactory.createStatementFromText("return;", null);
929         addToMethodCallLocation(statement);
930       }
931       else if (!myGenerateConditionalExit && exitStatementCopy != null) {
932         addToMethodCallLocation(exitStatementCopy);
933       }
934
935       if (!myNullConditionalCheck && !myNotNullConditionalCheck) {
936         declareNecessaryVariablesAfterCall(myOutputVariable);
937       }
938
939       deleteExtracted();
940     }
941     else {
942       PsiExpression expression2Replace = myExpression;
943       if (myExpression instanceof PsiAssignmentExpression) {
944         expression2Replace = ((PsiAssignmentExpression)myExpression).getRExpression();
945       } else if (myExpression instanceof PsiPostfixExpression || myExpression instanceof PsiPrefixExpression) {
946         final IElementType elementType = myExpression instanceof PsiPostfixExpression
947                                           ? ((PsiPostfixExpression)myExpression).getOperationTokenType()
948                                           : ((PsiPrefixExpression)myExpression).getOperationTokenType();
949         if (elementType == JavaTokenType.PLUSPLUS || elementType == JavaTokenType.MINUSMINUS) {
950           PsiExpression operand = myExpression instanceof PsiPostfixExpression ? ((PsiPostfixExpression)myExpression).getOperand() :
951                                   ((PsiPrefixExpression)myExpression).getOperand();
952           expression2Replace =
953             ((PsiBinaryExpression)myExpression.replace(myElementFactory.createExpressionFromText(operand.getText() + " + x", operand))).getROperand();
954         }
955
956       }
957       myExpression = (PsiExpression)IntroduceVariableBase.replace(expression2Replace, myMethodCall, myProject);
958       myMethodCall = PsiTreeUtil.getParentOfType(myExpression.findElementAt(myExpression.getText().indexOf(myMethodCall.getText())), PsiMethodCallExpression.class);
959       declareNecessaryVariablesAfterCall(myOutputVariable);
960     }
961
962     if (myAnchor instanceof PsiField) {
963       ((PsiField)myAnchor).normalizeDeclaration();
964     }
965
966     adjustFinalParameters(newMethod);
967     int i = 0;
968     for (VariableData data : myVariableDatum) {
969       if (!data.passAsParameter) continue;
970       final PsiParameter psiParameter = newMethod.getParameterList().getParameters()[i++];
971       final PsiType paramType = psiParameter.getType();
972       for (PsiReference reference : ReferencesSearch.search(psiParameter, new LocalSearchScope(body))){
973         final PsiElement element = reference.getElement();
974         if (element != null) {
975           final PsiElement parent = element.getParent();
976           if (parent instanceof PsiTypeCastExpression) {
977             final PsiTypeCastExpression typeCastExpression = (PsiTypeCastExpression)parent;
978             final PsiTypeElement castType = typeCastExpression.getCastType();
979             if (castType != null && Comparing.equal(castType.getType(), paramType)) {
980               RedundantCastUtil.removeCast(typeCastExpression);
981             }
982           }
983         }
984       }
985     }
986
987     if (myNullness != null &&
988         PsiUtil.resolveClassInType(newMethod.getReturnType()) != null &&
989         PropertiesComponent.getInstance(myProject).getBoolean(ExtractMethodDialog.EXTRACT_METHOD_GENERATE_ANNOTATIONS, true)) {
990       final NullableNotNullManager notNullManager = NullableNotNullManager.getInstance(myProject);
991       AddNullableNotNullAnnotationFix annotationFix;
992       switch (myNullness) {
993         case NOT_NULL:
994           annotationFix = new AddNullableNotNullAnnotationFix(notNullManager.getDefaultNotNull(), newMethod);
995           break;
996         case NULLABLE:
997           annotationFix = new AddNullableNotNullAnnotationFix(notNullManager.getDefaultNullable(), newMethod);
998           break;
999         default:
1000           annotationFix = null;
1001       }
1002       if (annotationFix != null) {
1003         annotationFix.invoke(myProject, myTargetClass.getContainingFile(), newMethod, newMethod);
1004       }
1005     }
1006
1007     myExtractedMethod = addExtractedMethod(newMethod);
1008     if (isNeedToChangeCallContext() && myNeedChangeContext) {
1009       ChangeContextUtil.decodeContextInfo(myExtractedMethod, myTargetClass, RefactoringChangeUtil.createThisExpression(myManager, null));
1010       if (myMethodCall.resolveMethod() != myExtractedMethod) {
1011         final PsiReferenceExpression methodExpression = myMethodCall.getMethodExpression();
1012         RefactoringChangeUtil.qualifyReference(methodExpression, myExtractedMethod, PsiUtil.getEnclosingStaticElement(methodExpression, myTargetClass) != null ? myTargetClass : null);
1013       }
1014     }
1015   }
1016
1017   protected PsiMethod addExtractedMethod(PsiMethod newMethod) {
1018     return (PsiMethod)myTargetClass.addAfter(newMethod, myAnchor);
1019   }
1020
1021   @Nullable
1022   private PsiStatement prepareMethodBody(PsiMethod newMethod, boolean doExtract) {
1023     PsiCodeBlock body = newMethod.getBody();
1024     if (myExpression != null) {
1025       declareNecessaryVariablesInsideBody(body);
1026       if (myHasExpressionOutput) {
1027         PsiReturnStatement returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return x;", null);
1028         final PsiExpression returnValue = RefactoringUtil.convertInitializerToNormalExpression(myExpression, myForcedReturnType);
1029         returnStatement.getReturnValue().replace(returnValue);
1030         body.add(returnStatement);
1031       }
1032       else {
1033         PsiExpressionStatement statement = (PsiExpressionStatement)myElementFactory.createStatementFromText("x;", null);
1034         statement.getExpression().replace(myExpression);
1035         body.add(statement);
1036       }
1037       return null;
1038     }
1039
1040     final boolean hasNormalExit = hasNormalExit();
1041     String outVariableName = myOutputVariable != null ? getNewVariableName(myOutputVariable) : null;
1042     PsiReturnStatement returnStatement;
1043     if (myNullConditionalCheck) {
1044       returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return null;", null);
1045     } else if (myOutputVariable != null) {
1046       returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return " + outVariableName + ";", null);
1047     }
1048     else if (myGenerateConditionalExit) {
1049       returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return true;", null);
1050     }
1051     else {
1052       returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return;", null);
1053     }
1054
1055     PsiStatement exitStatementCopy = !doExtract || myNotNullConditionalCheck ? null : myControlFlowWrapper.getExitStatementCopy(returnStatement, myElements);
1056
1057
1058     declareNecessaryVariablesInsideBody(body);
1059
1060     body.addRange(myElements[0], myElements[myElements.length - 1]);
1061     if (myNullConditionalCheck) {
1062       body.add(myElementFactory.createStatementFromText("return " + myOutputVariable.getName() + ";", null));
1063     }
1064     else if (myNotNullConditionalCheck) {
1065       body.add(myElementFactory.createStatementFromText("return null;", null));
1066     }
1067     else if (myGenerateConditionalExit) {
1068       body.add(myElementFactory.createStatementFromText("return false;", null));
1069     }
1070     else if (!myHasReturnStatement && hasNormalExit && myOutputVariable != null) {
1071       final PsiReturnStatement insertedReturnStatement = (PsiReturnStatement)body.add(returnStatement);
1072       if (myOutputVariables.length == 1) {
1073         final PsiExpression returnValue = insertedReturnStatement.getReturnValue();
1074         if (returnValue instanceof PsiReferenceExpression) {
1075           final PsiElement resolved = ((PsiReferenceExpression)returnValue).resolve();
1076           if (resolved instanceof PsiLocalVariable && Comparing.strEqual(((PsiVariable)resolved).getName(), outVariableName)) {
1077             final PsiStatement statement = PsiTreeUtil.getPrevSiblingOfType(insertedReturnStatement, PsiStatement.class);
1078             if (statement instanceof PsiDeclarationStatement) {
1079               final PsiElement[] declaredElements = ((PsiDeclarationStatement)statement).getDeclaredElements();
1080               if (ArrayUtil.find(declaredElements, resolved) != -1) {
1081                 InlineUtil.inlineVariable((PsiVariable)resolved, ((PsiVariable)resolved).getInitializer(),
1082                                           (PsiReferenceExpression)returnValue);
1083                 resolved.delete();
1084               }
1085             }
1086           }
1087         }
1088       }
1089     }
1090     else if (isArtificialOutputUsed()) {
1091       body.add(myElementFactory.createStatementFromText("return " + myArtificialOutputVariable.getName() + ";", null));
1092     }
1093     return exitStatementCopy;
1094   }
1095
1096   private boolean isArtificialOutputUsed() {
1097     return myArtificialOutputVariable != null && !PsiType.VOID.equals(myReturnType) && !myIsChainedConstructor;
1098   }
1099
1100   private boolean hasNormalExit() {
1101     try {
1102       PsiCodeBlock block = JavaPsiFacade.getElementFactory(myProject).createCodeBlock();
1103       block.addRange(myElements[0], myElements[myElements.length - 1]);
1104       ControlFlow flow = ControlFlowFactory.getInstance(myProject).getControlFlow(block, new LocalsControlFlowPolicy(block), false, false);
1105       return ControlFlowUtil.canCompleteNormally(flow, 0, flow.getSize());
1106     }
1107     catch (AnalysisCanceledException e) {
1108       //check incomplete code as simple as possible
1109       PsiElement lastElement = myElements[myElements.length - 1];
1110       if (!(lastElement instanceof PsiReturnStatement || lastElement instanceof PsiBreakStatement ||
1111             lastElement instanceof PsiContinueStatement)) {
1112         return true;
1113       }
1114       return false;
1115     }
1116   }
1117
1118   protected boolean isNeedToChangeCallContext() {
1119     return true;
1120   }
1121
1122   private void declareVariableAtMethodCallLocation(String name) {
1123     declareVariableAtMethodCallLocation(name, myReturnType);
1124   }
1125
1126   private String declareVariableAtMethodCallLocation(String name, PsiType type) {
1127     if (myControlFlowWrapper.getOutputVariables(false).length == 0) {
1128       PsiElement lastStatement = PsiTreeUtil.getNextSiblingOfType(myEnclosingBlockStatement != null ? myEnclosingBlockStatement : myElements[myElements.length - 1], PsiStatement.class);
1129       if (lastStatement != null) {
1130         name = JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(name, lastStatement, true);
1131       }
1132     }
1133     PsiDeclarationStatement statement = myElementFactory.createVariableDeclarationStatement(name, type, myMethodCall);
1134     statement =
1135       (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(addToMethodCallLocation(statement));
1136     PsiVariable var = (PsiVariable)statement.getDeclaredElements()[0];
1137     myMethodCall = (PsiMethodCallExpression)var.getInitializer();
1138     if (myOutputVariable != null) {
1139       var.getModifierList().replace(myOutputVariable.getModifierList());
1140     }
1141     return name;
1142   }
1143
1144   private void adjustFinalParameters(final PsiMethod method) throws IncorrectOperationException {
1145     final IncorrectOperationException[] exc = new IncorrectOperationException[1];
1146     exc[0] = null;
1147     final PsiParameter[] parameters = method.getParameterList().getParameters();
1148     if (parameters.length > 0) {
1149       if (CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS) {
1150         method.accept(new JavaRecursiveElementVisitor() {
1151
1152           @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
1153             final PsiElement resolved = expression.resolve();
1154             if (resolved != null) {
1155               final int index = ArrayUtil.find(parameters, resolved);
1156               if (index >= 0) {
1157                 final PsiParameter param = parameters[index];
1158                 if (param.hasModifierProperty(PsiModifier.FINAL) && PsiUtil.isAccessedForWriting(expression)) {
1159                   try {
1160                     PsiUtil.setModifierProperty(param, PsiModifier.FINAL, false);
1161                   }
1162                   catch (IncorrectOperationException e) {
1163                     exc[0] = e;
1164                   }
1165                 }
1166               }
1167             }
1168             super.visitReferenceExpression(expression);
1169           }
1170         });
1171       }
1172       else {
1173         method.accept(new JavaRecursiveElementVisitor() {
1174           @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
1175             final PsiElement resolved = expression.resolve();
1176             final int index = ArrayUtil.find(parameters, resolved);
1177             if (index >= 0) {
1178               final PsiParameter param = parameters[index];
1179               if (!param.hasModifierProperty(PsiModifier.FINAL) && RefactoringUtil.isInsideAnonymousOrLocal(expression, method)) {
1180                 try {
1181                   PsiUtil.setModifierProperty(param, PsiModifier.FINAL, true);
1182                 }
1183                 catch (IncorrectOperationException e) {
1184                   exc[0] = e;
1185                 }
1186               }
1187             }
1188             super.visitReferenceExpression(expression);
1189           }
1190         });
1191       }
1192       if (exc[0] != null) {
1193         throw exc[0];
1194       }
1195     }
1196   }
1197
1198   public List<Match> getDuplicates() {
1199     if (myIsChainedConstructor) {
1200       return filterChainedConstructorDuplicates(myDuplicates);
1201     }
1202     return myDuplicates;
1203   }
1204
1205   private static List<Match> filterChainedConstructorDuplicates(final List<Match> duplicates) {
1206     List<Match> result = new ArrayList<>();
1207     for(Match duplicate: duplicates) {
1208       final PsiElement matchStart = duplicate.getMatchStart();
1209       final PsiMethod method = PsiTreeUtil.getParentOfType(matchStart, PsiMethod.class);
1210       if (method != null && method.isConstructor()) {
1211         final PsiCodeBlock body = method.getBody();
1212         if (body != null) {
1213           final PsiStatement[] psiStatements = body.getStatements();
1214           if (psiStatements.length > 0 && matchStart == psiStatements [0]) {
1215             result.add(duplicate);
1216           }
1217         }
1218       }
1219     }
1220     return result;
1221   }
1222
1223   @Override
1224   public void prepareSignature(Match match) {
1225     MatchUtil.changeSignature(match, myExtractedMethod);
1226   }
1227
1228   public PsiElement processMatch(Match match) throws IncorrectOperationException {
1229     if (RefactoringUtil.isInStaticContext(match.getMatchStart(), myExtractedMethod.getContainingClass())) {
1230       PsiUtil.setModifierProperty(myExtractedMethod, PsiModifier.STATIC, true);
1231     }
1232     final PsiMethodCallExpression methodCallExpression = generateMethodCall(match.getInstanceExpression(), false);
1233
1234     ArrayList<VariableData> datas = new ArrayList<>();
1235     for (final VariableData variableData : myVariableDatum) {
1236       if (variableData.passAsParameter) {
1237         datas.add(variableData);
1238       }
1239     }
1240     final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myProject);
1241     for (VariableData data : datas) {
1242       final List<PsiElement> parameterValue = match.getParameterValues(data.variable);
1243       if (parameterValue != null) {
1244         for (PsiElement val : parameterValue) {
1245           if (val instanceof PsiExpression) {
1246             final PsiType exprType = ((PsiExpression)val).getType();
1247             if (exprType != null && !TypeConversionUtil.isAssignable(data.type, exprType)) {
1248               final PsiTypeCastExpression cast = (PsiTypeCastExpression)elementFactory.createExpressionFromText("(A)a", val);
1249               cast.getCastType().replace(elementFactory.createTypeElement(data.type));
1250               cast.getOperand().replace(val.copy());
1251               val = cast;
1252             }
1253           }
1254           methodCallExpression.getArgumentList().add(val);
1255         }
1256       } else {
1257         methodCallExpression.getArgumentList().add(myElementFactory.createExpressionFromText(data.variable.getName(), methodCallExpression));
1258       }
1259     }
1260     return match.replace(myExtractedMethod, methodCallExpression, myOutputVariable);
1261   }
1262
1263   protected void deleteExtracted() throws IncorrectOperationException {
1264     if (myEnclosingBlockStatement == null) {
1265       myElements[0].getParent().deleteChildRange(myElements[0], myElements[myElements.length - 1]);
1266     }
1267     else {
1268       myEnclosingBlockStatement.delete();
1269     }
1270   }
1271
1272   protected PsiElement addToMethodCallLocation(PsiStatement statement) throws IncorrectOperationException {
1273     if (myEnclosingBlockStatement == null) {
1274       PsiElement containingStatement = myElements[0] instanceof PsiComment ? myElements[0] : PsiTreeUtil.getParentOfType(myExpression != null ? myExpression : myElements[0], PsiStatement.class, false);
1275       if (containingStatement == null) {
1276         containingStatement = PsiTreeUtil.getParentOfType(myExpression != null ? myExpression : myElements[0], PsiComment.class, false);
1277       }
1278
1279       return containingStatement.getParent().addBefore(statement, containingStatement);
1280     }
1281     else {
1282       return myEnclosingBlockStatement.getParent().addBefore(statement, myEnclosingBlockStatement);
1283     }
1284   }
1285
1286   private void renameInputVariables() throws IncorrectOperationException {
1287     //when multiple input variables should have the same name, unique names are generated
1288     //without reverse, the second rename would rename variable without a prefix into second one though it was already renamed
1289     LocalSearchScope localSearchScope = null;
1290     for (int i = myVariableDatum.length - 1; i >= 0;  i--) {
1291       VariableData data = myVariableDatum[i];
1292       PsiVariable variable = data.variable;
1293       if (!data.name.equals(variable.getName()) || variable instanceof PsiField) {
1294         if (localSearchScope == null) {
1295           localSearchScope = new LocalSearchScope(myElements);
1296         }
1297
1298         for (PsiReference reference : ReferencesSearch.search(variable, localSearchScope)) {
1299           reference.handleElementRename(data.name);
1300
1301           final PsiElement element = reference.getElement();
1302           if (element instanceof PsiReferenceExpression) {
1303             final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)element;
1304             final PsiExpression qualifierExpression = referenceExpression.getQualifierExpression();
1305             if (qualifierExpression instanceof PsiThisExpression || qualifierExpression instanceof PsiSuperExpression) {
1306               referenceExpression.setQualifierExpression(null);
1307             }
1308           }
1309         }
1310       }
1311     }
1312   }
1313
1314   public PsiClass getTargetClass() {
1315     return myTargetClass;
1316   }
1317
1318   public PsiType getReturnType() {
1319     return myReturnType;
1320   }
1321
1322   private PsiMethod generateEmptyMethod() throws IncorrectOperationException {
1323     return generateEmptyMethod(myMethodName);
1324   }
1325
1326   public PsiMethod generateEmptyMethod(String methodName) throws IncorrectOperationException {
1327     PsiMethod newMethod;
1328     if (myIsChainedConstructor) {
1329       newMethod = myElementFactory.createConstructor();
1330     }
1331     else {
1332       newMethod = myElementFactory.createMethod(methodName, myReturnType);
1333       PsiUtil.setModifierProperty(newMethod, PsiModifier.STATIC, isStatic());
1334     }
1335     PsiUtil.setModifierProperty(newMethod, myMethodVisibility, true);
1336     if (getTypeParameterList() != null) {
1337       newMethod.getTypeParameterList().replace(getTypeParameterList());
1338     }
1339     PsiCodeBlock body = newMethod.getBody();
1340     LOG.assertTrue(body != null);
1341
1342     boolean isFinal = CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS;
1343     PsiParameterList list = newMethod.getParameterList();
1344     for (VariableData data : myVariableDatum) {
1345       if (data.passAsParameter) {
1346         PsiParameter parm = myElementFactory.createParameter(data.name, data.type);
1347         copyParamAnnotations(parm);
1348         if (isFinal) {
1349           PsiUtil.setModifierProperty(parm, PsiModifier.FINAL, true);
1350         }
1351         list.add(parm);
1352       }
1353       else {
1354         @NonNls StringBuilder buffer = new StringBuilder();
1355         if (isFinal) {
1356           buffer.append("final ");
1357         }
1358         buffer.append("int ");
1359         buffer.append(data.name);
1360         buffer.append("=;");
1361         String text = buffer.toString();
1362
1363         PsiDeclarationStatement declaration = (PsiDeclarationStatement)myElementFactory.createStatementFromText(text, null);
1364         declaration = (PsiDeclarationStatement)myStyleManager.reformat(declaration);
1365         final PsiTypeElement typeElement = myElementFactory.createTypeElement(data.type);
1366         ((PsiVariable)declaration.getDeclaredElements()[0]).getTypeElement().replace(typeElement);
1367         body.add(declaration);
1368       }
1369     }
1370
1371     PsiReferenceList throwsList = newMethod.getThrowsList();
1372     for (PsiClassType exception : getThrownExceptions()) {
1373       throwsList.add(JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createReferenceElementByType(exception));
1374     }
1375
1376     if (myTargetClass.isInterface() && PsiUtil.isLanguageLevel8OrHigher(myTargetClass)) {
1377       final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(myCodeFragmentMember, PsiMethod.class, false);
1378       if (containingMethod != null && containingMethod.hasModifierProperty(PsiModifier.DEFAULT)) {
1379         PsiUtil.setModifierProperty(newMethod, PsiModifier.DEFAULT, true);
1380       }
1381     }
1382     return (PsiMethod)myStyleManager.reformat(newMethod);
1383   }
1384
1385   private void copyParamAnnotations(PsiParameter parm) {
1386     final PsiVariable variable = PsiResolveHelper.SERVICE.getInstance(myProject).resolveReferencedVariable(parm.getName(), myElements[0]);
1387     if (variable instanceof PsiParameter) {
1388       final PsiModifierList modifierList = variable.getModifierList();
1389       if (modifierList != null) {
1390         for (PsiAnnotation annotation : modifierList.getAnnotations()) {
1391           if (SuppressWarnings.class.getName().equals(annotation.getQualifiedName())) continue;
1392           final PsiModifierList parmModifierList = parm.getModifierList();
1393           LOG.assertTrue(parmModifierList != null, parm);
1394           parmModifierList.add(annotation);
1395         }
1396       }
1397     }
1398   }
1399
1400   @NotNull
1401   protected PsiMethodCallExpression generateMethodCall(PsiExpression instanceQualifier, final boolean generateArgs) throws IncorrectOperationException {
1402     @NonNls StringBuilder buffer = new StringBuilder();
1403
1404     final boolean skipInstanceQualifier;
1405     if (myIsChainedConstructor) {
1406       skipInstanceQualifier = true;
1407       buffer.append(PsiKeyword.THIS);
1408     }
1409     else {
1410       skipInstanceQualifier = instanceQualifier == null || instanceQualifier instanceof PsiThisExpression;
1411       if (skipInstanceQualifier) {
1412         if (isNeedToChangeCallContext() && myNeedChangeContext) {
1413           boolean needsThisQualifier = false;
1414           PsiElement parent = myCodeFragmentMember;
1415           while (!myTargetClass.equals(parent)) {
1416             if (parent instanceof PsiMethod) {
1417               String methodName = ((PsiMethod)parent).getName();
1418               if (methodName.equals(myMethodName)) {
1419                 needsThisQualifier = true;
1420                 break;
1421               }
1422             }
1423             parent = parent.getParent();
1424           }
1425           if (needsThisQualifier) {
1426             buffer.append(myTargetClass.getName());
1427             buffer.append(".this.");
1428           }
1429         }
1430       }
1431       else {
1432         buffer.append("qqq.");
1433       }
1434
1435       buffer.append(myMethodName);
1436     }
1437     buffer.append("(");
1438     if (generateArgs) {
1439       int count = 0;
1440       for (VariableData data : myVariableDatum) {
1441         if (data.passAsParameter) {
1442           if (count > 0) {
1443             buffer.append(",");
1444           }
1445           myInputVariables.appendCallArguments(data, buffer);
1446           count++;
1447         }
1448       }
1449     }
1450     buffer.append(")");
1451     String text = buffer.toString();
1452
1453     PsiMethodCallExpression expr = (PsiMethodCallExpression)myElementFactory.createExpressionFromText(text, null);
1454     expr = (PsiMethodCallExpression)myStyleManager.reformat(expr);
1455     if (!skipInstanceQualifier) {
1456       PsiExpression qualifierExpression = expr.getMethodExpression().getQualifierExpression();
1457       LOG.assertTrue(qualifierExpression != null);
1458       qualifierExpression.replace(instanceQualifier);
1459     }
1460     return (PsiMethodCallExpression)JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(expr);
1461   }
1462
1463   private boolean chooseTargetClass(PsiElement codeFragment, final Pass<ExtractMethodProcessor> extractPass) throws PrepareFailedException {
1464     final List<PsiVariable> inputVariables = myControlFlowWrapper.getInputVariables(codeFragment, myElements, myOutputVariables);
1465
1466     myNeedChangeContext = false;
1467     myTargetClass = myCodeFragmentMember instanceof PsiMember
1468                     ? ((PsiMember)myCodeFragmentMember).getContainingClass()
1469                     : PsiTreeUtil.getParentOfType(myCodeFragmentMember, PsiClass.class);
1470     if (myTargetClass == null) {
1471       LOG.error(myElements[0].getContainingFile());
1472     }
1473     if (!shouldAcceptCurrentTarget(extractPass, myTargetClass)) {
1474
1475       final LinkedHashMap<PsiClass, List<PsiVariable>> classes = new LinkedHashMap<>();
1476       final PsiElementProcessor<PsiClass> processor = new PsiElementProcessor<PsiClass>() {
1477         @Override
1478         public boolean execute(@NotNull PsiClass selectedClass) {
1479           AnonymousTargetClassPreselectionUtil.rememberSelection(selectedClass, myTargetClass);
1480           final List<PsiVariable> array = classes.get(selectedClass);
1481           myNeedChangeContext = myTargetClass != selectedClass;
1482           myTargetClass = selectedClass;
1483           if (array != null) {
1484             for (PsiVariable variable : array) {
1485               if (!inputVariables.contains(variable)) {
1486                 inputVariables.addAll(array);
1487               }
1488             }
1489           }
1490           try {
1491             return applyChosenClassAndExtract(inputVariables, extractPass);
1492           }
1493           catch (PrepareFailedException e) {
1494             if (myShowErrorDialogs) {
1495               CommonRefactoringUtil
1496                 .showErrorHint(myProject, myEditor, e.getMessage(), ExtractMethodHandler.REFACTORING_NAME, HelpID.EXTRACT_METHOD);
1497               ExtractMethodHandler.highlightPrepareError(e, e.getFile(), myEditor, myProject);
1498             }
1499             return false;
1500           }
1501         }
1502       };
1503
1504       classes.put(myTargetClass, null);
1505       PsiElement target = myTargetClass.getParent();
1506       PsiElement targetMember = myTargetClass;
1507       while (true) {
1508         if (target instanceof PsiFile) break;
1509         if (target instanceof PsiClass) {
1510           boolean success = true;
1511           final List<PsiVariable> array = new ArrayList<>();
1512           for (PsiElement el : myElements) {
1513             if (!ControlFlowUtil.collectOuterLocals(array, el, myCodeFragmentMember, targetMember)) {
1514               success = false;
1515               break;
1516             }
1517           }
1518           if (success) {
1519             classes.put((PsiClass)target, array);
1520             if (shouldAcceptCurrentTarget(extractPass, target)) {
1521               return processor.execute((PsiClass)target);
1522             }
1523           }
1524         }
1525         targetMember = target;
1526         target = target.getParent();
1527       }
1528
1529       if (classes.size() > 1) {
1530         final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]);
1531         final PsiClass preselection = AnonymousTargetClassPreselectionUtil.getPreselection(classes.keySet(), psiClasses[0]);
1532         NavigationUtil.getPsiElementPopup(psiClasses, PsiClassListCellRenderer.INSTANCE, "Choose Destination Class", processor, preselection)
1533           .showInBestPositionFor(myEditor);
1534         return true;
1535       }
1536     }
1537
1538     return applyChosenClassAndExtract(inputVariables, extractPass);
1539   }
1540
1541   private void declareNecessaryVariablesInsideBody(PsiCodeBlock body) throws IncorrectOperationException {
1542     List<PsiVariable> usedVariables = myControlFlowWrapper.getUsedVariablesInBody(ControlFlowUtil.findCodeFragment(myElements[0]), myOutputVariables);
1543     for (PsiVariable variable : usedVariables) {
1544       boolean toDeclare = !isDeclaredInside(variable) && myInputVariables.toDeclareInsideBody(variable);
1545       if (toDeclare) {
1546         String name = variable.getName();
1547         PsiDeclarationStatement statement = myElementFactory.createVariableDeclarationStatement(name, variable.getType(), null);
1548         body.add(statement);
1549       }
1550     }
1551
1552     if (myArtificialOutputVariable instanceof PsiField && !myIsChainedConstructor) {
1553       body.add(myElementFactory.createVariableDeclarationStatement(myArtificialOutputVariable.getName(), myArtificialOutputVariable.getType(), null));
1554     }
1555   }
1556
1557   protected void declareNecessaryVariablesAfterCall(PsiVariable outputVariable) throws IncorrectOperationException {
1558     if (myHasExpressionOutput) return;
1559     List<PsiVariable> usedVariables = myControlFlowWrapper.getUsedVariables();
1560     Collection<ControlFlowUtil.VariableInfo> reassigned = myControlFlowWrapper.getInitializedTwice();
1561     for (PsiVariable variable : usedVariables) {
1562       boolean toDeclare = isDeclaredInside(variable) && !variable.equals(outputVariable);
1563       if (toDeclare) {
1564         String name = variable.getName();
1565         PsiDeclarationStatement statement = myElementFactory.createVariableDeclarationStatement(name, variable.getType(), null);
1566         if (reassigned.contains(new ControlFlowUtil.VariableInfo(variable, null))) {
1567           final PsiElement[] psiElements = statement.getDeclaredElements();
1568           assert psiElements.length > 0;
1569           PsiVariable var = (PsiVariable) psiElements [0];
1570           PsiUtil.setModifierProperty(var, PsiModifier.FINAL, false);
1571         }
1572         addToMethodCallLocation(statement);
1573       }
1574     }
1575   }
1576
1577   public PsiMethodCallExpression getMethodCall() {
1578     return myMethodCall;
1579   }
1580
1581   public void setMethodCall(PsiMethodCallExpression methodCall) {
1582     myMethodCall = methodCall;
1583   }
1584
1585   public boolean isDeclaredInside(PsiVariable variable) {
1586     if (variable instanceof ImplicitVariable) return false;
1587     int startOffset;
1588     int endOffset;
1589     if (myExpression != null) {
1590       final TextRange range = myExpression.getTextRange();
1591       startOffset = range.getStartOffset();
1592       endOffset = range.getEndOffset();
1593     } else {
1594       startOffset = myElements[0].getTextRange().getStartOffset();
1595       endOffset = myElements[myElements.length - 1].getTextRange().getEndOffset();
1596     }
1597     PsiIdentifier nameIdentifier = variable.getNameIdentifier();
1598     if (nameIdentifier == null) return false;
1599     final TextRange range = nameIdentifier.getTextRange();
1600     if (range == null) return false;
1601     int offset = range.getStartOffset();
1602     return startOffset <= offset && offset <= endOffset;
1603   }
1604
1605   private String getNewVariableName(PsiVariable variable) {
1606     for (VariableData data : myVariableDatum) {
1607       if (data.variable.equals(variable)) {
1608         return data.name;
1609       }
1610     }
1611     return variable.getName();
1612   }
1613
1614   private static boolean shouldAcceptCurrentTarget(Pass<ExtractMethodProcessor> extractPass, PsiElement target) {
1615     return extractPass == null && !(target instanceof PsiAnonymousClass);
1616   }
1617
1618   private boolean applyChosenClassAndExtract(List<PsiVariable> inputVariables, @Nullable Pass<ExtractMethodProcessor> extractPass)
1619     throws PrepareFailedException {
1620     myStatic = shouldBeStatic();
1621     final Set<PsiField> fields = new LinkedHashSet<>();
1622     if (!PsiUtil.isLocalOrAnonymousClass(myTargetClass) && (myTargetClass.getContainingClass() == null || myTargetClass.hasModifierProperty(PsiModifier.STATIC))) {
1623       boolean canBeStatic = true;
1624       if (myTargetClass.isInterface()) {
1625         final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(myCodeFragmentMember, PsiMethod.class, false);
1626         canBeStatic = containingMethod == null || containingMethod.hasModifierProperty(PsiModifier.STATIC);
1627       }
1628       if (canBeStatic) {
1629         ElementNeedsThis needsThis = new ElementNeedsThis(myTargetClass) {
1630           @Override
1631           protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) {
1632             if (classMember instanceof PsiField && !classMember.hasModifierProperty(PsiModifier.STATIC)) {
1633               final PsiExpression expression = PsiTreeUtil.getParentOfType(classMemberReference, PsiExpression.class, false);
1634               if (expression == null || !PsiUtil.isAccessedForWriting(expression)) {
1635                 fields.add((PsiField)classMember);
1636                 return;
1637               }
1638             }
1639             super.visitClassMemberReferenceElement(classMember, classMemberReference);
1640           }
1641         };
1642         for (int i = 0; i < myElements.length && !needsThis.usesMembers(); i++) {
1643           PsiElement element = myElements[i];
1644           element.accept(needsThis);
1645         }
1646         myCanBeStatic = !needsThis.usesMembers();
1647       }
1648       else {
1649         myCanBeStatic = false;
1650       }
1651     }
1652     else {
1653       myCanBeStatic = false;
1654     }
1655
1656     myInputVariables = new InputVariables(inputVariables, myProject, new LocalSearchScope(myElements), isFoldingApplicable());
1657     myInputVariables.setUsedInstanceFields(fields);
1658
1659     if (!checkExitPoints()){
1660       return false;
1661     }
1662
1663     checkCanBeChainedConstructor();
1664
1665     if (extractPass != null) {
1666       extractPass.pass(this);
1667     }
1668     return true;
1669   }
1670
1671   protected boolean isFoldingApplicable() {
1672     return true;
1673   }
1674
1675   private void chooseAnchor() {
1676     myAnchor = myCodeFragmentMember;
1677     while (!myAnchor.getParent().equals(myTargetClass)) {
1678       myAnchor = myAnchor.getParent();
1679     }
1680   }
1681
1682   private void showMultipleExitPointsMessage() {
1683     if (myShowErrorDialogs) {
1684       HighlightManager highlightManager = HighlightManager.getInstance(myProject);
1685       PsiStatement[] exitStatementsArray = myExitStatements.toArray(new PsiStatement[myExitStatements.size()]);
1686       EditorColorsManager manager = EditorColorsManager.getInstance();
1687       TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
1688       highlightManager.addOccurrenceHighlights(myEditor, exitStatementsArray, attributes, true, null);
1689       String message = RefactoringBundle
1690         .getCannotRefactorMessage(RefactoringBundle.message("there.are.multiple.exit.points.in.the.selected.code.fragment"));
1691       CommonRefactoringUtil.showErrorHint(myProject, myEditor, message, myRefactoringName, myHelpId);
1692       WindowManager.getInstance().getStatusBar(myProject).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
1693     }
1694   }
1695
1696   private void showMultipleOutputMessage(PsiType expressionType) {
1697     if (myShowErrorDialogs) {
1698       StringBuilder buffer = new StringBuilder();
1699       buffer.append(RefactoringBundle.getCannotRefactorMessage(
1700         RefactoringBundle.message("there.are.multiple.output.values.for.the.selected.code.fragment")));
1701       buffer.append("\n");
1702       if (myHasExpressionOutput) {
1703         buffer.append("    ").append(RefactoringBundle.message("expression.result")).append(": ");
1704         buffer.append(PsiFormatUtil.formatType(expressionType, 0, PsiSubstitutor.EMPTY));
1705         buffer.append(",\n");
1706       }
1707       if (myGenerateConditionalExit) {
1708         buffer.append("    ").append(RefactoringBundle.message("boolean.method.result"));
1709         buffer.append(",\n");
1710       }
1711       for (int i = 0; i < myOutputVariables.length; i++) {
1712         PsiVariable var = myOutputVariables[i];
1713         buffer.append("    ");
1714         buffer.append(var.getName());
1715         buffer.append(" : ");
1716         buffer.append(PsiFormatUtil.formatType(var.getType(), 0, PsiSubstitutor.EMPTY));
1717         if (i < myOutputVariables.length - 1) {
1718           buffer.append(",\n");
1719         }
1720         else {
1721           buffer.append(".");
1722         }
1723       }
1724       buffer.append("\nWould you like to Extract Method Object?");
1725
1726       String message = buffer.toString();
1727
1728       if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message);
1729       RefactoringMessageDialog dialog = new RefactoringMessageDialog(myRefactoringName, message, myHelpId, "OptionPane.errorIcon", true,
1730                                                                      myProject);
1731       if (dialog.showAndGet()) {
1732         new ExtractMethodObjectHandler()
1733           .invoke(myProject, myEditor, myTargetClass.getContainingFile(), DataManager.getInstance().getDataContext());
1734       }
1735     }
1736   }
1737
1738   public PsiMethod getExtractedMethod() {
1739     return myExtractedMethod;
1740   }
1741
1742   public Boolean hasDuplicates() {
1743     List<Match> duplicates = getDuplicates();
1744     if (duplicates != null && !duplicates.isEmpty()) {
1745       return true;
1746     }
1747
1748     if (myExtractedMethod != null) {
1749       final ExtractMethodSignatureSuggester suggester = new ExtractMethodSignatureSuggester(myProject, myExtractedMethod, myMethodCall, myVariableDatum);
1750       duplicates = suggester.getDuplicates(myExtractedMethod, myMethodCall, myInputVariables.getFolding());
1751       if (duplicates != null && !duplicates.isEmpty()) {
1752         myDuplicates      = duplicates;
1753         myExtractedMethod = suggester.getExtractedMethod();
1754         myMethodCall      = suggester.getMethodCall();
1755         myVariableDatum   = suggester.getVariableData();
1756
1757         final List<PsiVariable> outputVariables = new ArrayList<>();
1758         for (PsiReturnStatement statement : PsiUtil.findReturnStatements(myExtractedMethod)) {
1759           final PsiExpression returnValue = statement.getReturnValue();
1760           if (returnValue instanceof PsiReferenceExpression) {
1761             final PsiElement resolve = ((PsiReferenceExpression)returnValue).resolve();
1762             if (resolve instanceof PsiLocalVariable) {
1763               outputVariables.add((PsiVariable)resolve);
1764             }
1765           }
1766         }
1767
1768         if (outputVariables.size() == 1) {
1769           myOutputVariable = outputVariables.get(0);
1770         }
1771
1772         return null;
1773       }
1774     }
1775     return false;
1776   }
1777
1778   public boolean hasDuplicates(Set<VirtualFile> files) {
1779     final DuplicatesFinder finder = initDuplicates();
1780
1781     final Boolean hasDuplicates = hasDuplicates();
1782     if (hasDuplicates == null || hasDuplicates) return true;
1783     if (finder != null) {
1784       final PsiManager psiManager = PsiManager.getInstance(myProject);
1785       for (VirtualFile file : files) {
1786         if (!finder.findDuplicates(psiManager.findFile(file)).isEmpty()) return true;
1787       }
1788     }
1789     return false;
1790   }
1791
1792   @Nullable
1793   public String getConfirmDuplicatePrompt(Match match) {
1794     final boolean needToBeStatic = RefactoringUtil.isInStaticContext(match.getMatchStart(), myExtractedMethod.getContainingClass());
1795     final String changedSignature = MatchUtil
1796       .getChangedSignature(match, myExtractedMethod, needToBeStatic, VisibilityUtil.getVisibilityStringToDisplay(myExtractedMethod));
1797     if (changedSignature != null) {
1798       return RefactoringBundle.message("replace.this.code.fragment.and.change.signature", changedSignature);
1799     }
1800     if (needToBeStatic && !myExtractedMethod.hasModifierProperty(PsiModifier.STATIC)) {
1801       return RefactoringBundle.message("replace.this.code.fragment.and.make.method.static");
1802     }
1803     return null;
1804   }
1805
1806   @Override
1807   public String getReplaceDuplicatesTitle(int idx, int size) {
1808     return RefactoringBundle.message("process.duplicates.title", idx, size);
1809   }
1810
1811   public InputVariables getInputVariables() {
1812     return myInputVariables;
1813   }
1814
1815   public PsiTypeParameterList getTypeParameterList() {
1816     return myTypeParameterList;
1817   }
1818
1819   public PsiClassType[] getThrownExceptions() {
1820     return myThrownExceptions;
1821   }
1822
1823   public boolean isStatic() {
1824     return myStatic;
1825   }
1826
1827   public boolean isCanBeStatic() {
1828     return myCanBeStatic;
1829   }
1830
1831   public PsiElement[] getElements() {
1832     return myElements;
1833   }
1834
1835   public PsiVariable[] getOutputVariables() {
1836     return myOutputVariables;
1837   }
1838 }