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