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