remove redundant casts when extract changed parameter type accordingly (IDEA-79743)
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / extractMethod / ExtractMethodProcessor.java
1 /*
2  * Copyright 2000-2011 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.ExceptionUtil;
20 import com.intellij.codeInsight.NullableNotNullManager;
21 import com.intellij.codeInsight.highlighting.HighlightManager;
22 import com.intellij.codeInsight.intention.impl.AddNullableAnnotationFix;
23 import com.intellij.codeInsight.navigation.NavigationUtil;
24 import com.intellij.codeInspection.dataFlow.RunnerResult;
25 import com.intellij.codeInspection.dataFlow.StandardDataFlowRunner;
26 import com.intellij.codeInspection.dataFlow.StandardInstructionVisitor;
27 import com.intellij.codeInspection.dataFlow.instructions.BranchingInstruction;
28 import com.intellij.codeInspection.dataFlow.instructions.Instruction;
29 import com.intellij.ide.DataManager;
30 import com.intellij.ide.util.PsiClassListCellRenderer;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.diagnostic.Logger;
33 import com.intellij.openapi.editor.Editor;
34 import com.intellij.openapi.editor.LogicalPosition;
35 import com.intellij.openapi.editor.ScrollType;
36 import com.intellij.openapi.editor.colors.EditorColors;
37 import com.intellij.openapi.editor.colors.EditorColorsManager;
38 import com.intellij.openapi.editor.markup.TextAttributes;
39 import com.intellij.openapi.progress.ProgressManager;
40 import com.intellij.openapi.project.Project;
41 import com.intellij.openapi.util.Comparing;
42 import com.intellij.openapi.util.Pair;
43 import com.intellij.openapi.util.Pass;
44 import com.intellij.openapi.util.TextRange;
45 import com.intellij.openapi.vfs.VirtualFile;
46 import com.intellij.openapi.wm.WindowManager;
47 import com.intellij.psi.*;
48 import com.intellij.psi.codeStyle.CodeStyleManager;
49 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
50 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
51 import com.intellij.psi.controlFlow.ControlFlowUtil;
52 import com.intellij.psi.search.GlobalSearchScope;
53 import com.intellij.psi.search.LocalSearchScope;
54 import com.intellij.psi.search.PsiElementProcessor;
55 import com.intellij.psi.search.SearchScope;
56 import com.intellij.psi.search.searches.ReferencesSearch;
57 import com.intellij.psi.tree.IElementType;
58 import com.intellij.psi.util.*;
59 import com.intellij.refactoring.HelpID;
60 import com.intellij.refactoring.RefactoringBundle;
61 import com.intellij.refactoring.extractMethodObject.ExtractMethodObjectHandler;
62 import com.intellij.refactoring.introduceField.ElementToWorkOn;
63 import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
64 import com.intellij.refactoring.util.*;
65 import com.intellij.refactoring.util.classMembers.ElementNeedsThis;
66 import com.intellij.refactoring.util.duplicates.DuplicatesFinder;
67 import com.intellij.refactoring.util.duplicates.Match;
68 import com.intellij.refactoring.util.duplicates.MatchProvider;
69 import com.intellij.refactoring.util.duplicates.VariableReturnValue;
70 import com.intellij.util.ArrayUtil;
71 import com.intellij.util.IncorrectOperationException;
72 import com.intellij.util.Processor;
73 import com.intellij.util.VisibilityUtil;
74 import org.jetbrains.annotations.NonNls;
75 import org.jetbrains.annotations.NotNull;
76 import org.jetbrains.annotations.Nullable;
77 import org.jetbrains.annotations.TestOnly;
78
79 import java.util.*;
80
81 public class ExtractMethodProcessor implements MatchProvider {
82   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractMethod.ExtractMethodProcessor");
83
84   protected final Project myProject;
85   private final Editor myEditor;
86   protected final PsiElement[] myElements;
87   private final PsiBlockStatement myEnclosingBlockStatement;
88   private final PsiType myForcedReturnType;
89   private final String myRefactoringName;
90   protected final String myInitialMethodName;
91   private final String myHelpId;
92
93   private final PsiManager myManager;
94   private final PsiElementFactory myElementFactory;
95   private final CodeStyleManager myStyleManager;
96
97   private PsiExpression myExpression;
98
99   private PsiElement myCodeFragmentMember; // parent of myCodeFragment
100
101   protected String myMethodName; // name for extracted method
102   protected PsiType myReturnType; // return type for extracted method
103   protected PsiTypeParameterList myTypeParameterList; //type parameter list of extracted method
104   private ParameterTablePanel.VariableData[] myVariableDatum; // parameter data for extracted method
105   protected PsiClassType[] myThrownExceptions; // exception to declare as thrown by extracted method
106   protected boolean myStatic; // whether to declare extracted method static
107
108   protected PsiClass myTargetClass; // class to create the extracted method in
109   private PsiElement myAnchor; // anchor to insert extracted method after it
110
111   protected ControlFlowWrapper myControlFlowWrapper;
112   protected InputVariables myInputVariables; // input variables
113   protected PsiVariable[] myOutputVariables; // output variables
114   protected PsiVariable myOutputVariable; // the only output variable
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 DuplicatesFinder myDuplicatesFinder;
127   private List<Match> myDuplicates;
128   @PsiModifier.ModifierConstant private String myMethodVisibility = PsiModifier.PRIVATE;
129   protected boolean myGenerateConditionalExit;
130   private PsiStatement myFirstExitStatementCopy;
131   private PsiMethod myExtractedMethod;
132   private PsiMethodCallExpression myMethodCall;
133   private boolean myNullConditionalCheck = false;
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.length == 1 && !(elements[0] instanceof PsiBlockStatement)) {
145       myElements = elements;
146       myEnclosingBlockStatement = null;
147     }
148     else {
149       myEnclosingBlockStatement = (PsiBlockStatement)elements[0];
150       PsiElement[] codeBlockChildren = myEnclosingBlockStatement.getCodeBlock().getChildren();
151       myElements = processCodeBlockChildren(codeBlockChildren);
152     }
153     myForcedReturnType = forcedReturnType;
154     myRefactoringName = refactoringName;
155     myInitialMethodName = initialMethodName;
156     myHelpId = helpId;
157
158     myManager = PsiManager.getInstance(myProject);
159     myElementFactory = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory();
160     myStyleManager = CodeStyleManager.getInstance(myProject);
161   }
162
163   private static PsiElement[] processCodeBlockChildren(PsiElement[] codeBlockChildren) {
164     int resultLast = codeBlockChildren.length;
165
166     if (codeBlockChildren.length == 0) return PsiElement.EMPTY_ARRAY;
167
168     final PsiElement first = codeBlockChildren[0];
169     int resultStart = 0;
170     if (first instanceof PsiJavaToken && ((PsiJavaToken)first).getTokenType() == JavaTokenType.LBRACE) {
171       resultStart++;
172     }
173     final PsiElement last = codeBlockChildren[codeBlockChildren.length - 1];
174     if (last instanceof PsiJavaToken && ((PsiJavaToken)last).getTokenType() == JavaTokenType.RBRACE) {
175       resultLast--;
176     }
177     final ArrayList<PsiElement> result = new ArrayList<PsiElement>();
178     for (int i = resultStart; i < resultLast; i++) {
179       PsiElement element = codeBlockChildren[i];
180       if (!(element instanceof PsiWhiteSpace)) {
181         result.add(element);
182       }
183     }
184
185     return PsiUtilCore.toPsiElementArray(result);
186   }
187
188   /**
189    * Method for test purposes
190    */
191   public void setShowErrorDialogs(boolean showErrorDialogs) {
192     myShowErrorDialogs = showErrorDialogs;
193   }
194
195   public void setChainedConstructor(final boolean isChainedConstructor) {
196     myIsChainedConstructor = isChainedConstructor;
197   }
198
199
200   public boolean prepare() throws PrepareFailedException {
201     return prepare(null);
202   }
203   
204   /**
205    * Invoked in atomic action
206    */
207   public boolean prepare(@Nullable Pass<ExtractMethodProcessor> pass) throws PrepareFailedException {
208     myExpression = null;
209     if (myElements.length == 1 && myElements[0] instanceof PsiExpression) {
210       final PsiExpression expression = (PsiExpression)myElements[0];
211       if (expression instanceof PsiAssignmentExpression && expression.getParent() instanceof PsiExpressionStatement) {
212         myElements[0] = expression.getParent();
213       }
214       else {
215         myExpression = expression;
216       }
217     }
218
219     final PsiElement codeFragment = ControlFlowUtil.findCodeFragment(myElements[0]);
220     myCodeFragmentMember = codeFragment.getUserData(ElementToWorkOn.PARENT);
221     if (myCodeFragmentMember == null) {
222       myCodeFragmentMember = codeFragment.getParent();
223     }
224     if (myCodeFragmentMember == null) {
225       myCodeFragmentMember = ControlFlowUtil.findCodeFragment(codeFragment.getContext()).getParent();
226     }
227
228     myControlFlowWrapper = new ControlFlowWrapper(myProject, codeFragment, myElements);
229
230     try {
231       myExitStatements = myControlFlowWrapper.prepareExitStatements(myElements);
232       if (myControlFlowWrapper.isGenerateConditionalExit()) {
233         myGenerateConditionalExit = true;
234       } else {
235         myHasReturnStatement = myExpression == null && myControlFlowWrapper.isReturnPresentBetween();
236       }
237       myFirstExitStatementCopy = myControlFlowWrapper.getFirstExitStatementCopy();
238     }
239     catch (ControlFlowWrapper.ExitStatementsNotSameException e) {
240       myExitStatements = myControlFlowWrapper.getExitStatements();
241       showMultipleExitPointsMessage();
242       return false;
243     }
244
245     myOutputVariables = myControlFlowWrapper.getOutputVariables();
246
247     checkCanBeChainedConstructor();
248
249     return chooseTargetClass(codeFragment, pass);
250   }
251
252   private boolean checkExitPoints() throws PrepareFailedException {
253     PsiType expressionType = null;
254     if (myExpression != null) {
255       if (myForcedReturnType != null) {
256         expressionType = myForcedReturnType;
257       }
258       else {
259         expressionType = RefactoringUtil.getTypeByExpressionWithExpectedType(myExpression);
260       }
261     }
262     if (expressionType == null) {
263       expressionType = PsiType.VOID;
264     }
265     myHasExpressionOutput = expressionType != PsiType.VOID;
266
267     PsiType returnStatementType = null;
268     if (myHasReturnStatement) {
269       returnStatementType = myCodeFragmentMember instanceof PsiMethod ? ((PsiMethod)myCodeFragmentMember).getReturnType() : null;
270     }
271     myHasReturnStatementOutput = returnStatementType != null && returnStatementType != PsiType.VOID;
272
273     if (myGenerateConditionalExit && myOutputVariables.length == 1) {
274       if (!(myOutputVariables[0].getType() instanceof PsiPrimitiveType)) {
275         myNullConditionalCheck = true;
276         for (PsiStatement exitStatement : myExitStatements) {
277           if (exitStatement instanceof PsiReturnStatement) {
278             final PsiExpression returnValue = ((PsiReturnStatement)exitStatement).getReturnValue();
279             myNullConditionalCheck &= returnValue == null ||
280                                       returnValue instanceof PsiLiteralExpression && PsiType.NULL.equals(returnValue.getType());
281           }
282         }
283         myNullConditionalCheck &= isNotNull(myOutputVariables[0]);
284       }
285     }
286
287     if (!myHasReturnStatementOutput && checkOutputVariablesCount() && !myNullConditionalCheck) {
288       showMultipleOutputMessage(expressionType);
289       return false;
290     }
291
292     myOutputVariable = myOutputVariables.length > 0 ? myOutputVariables[0] : null;
293     if (myHasReturnStatementOutput) {
294       myReturnType = returnStatementType;
295     }
296     else if (myOutputVariable != null) {
297       myReturnType = myOutputVariable.getType();
298     }
299     else if (myGenerateConditionalExit) {
300       myReturnType = PsiType.BOOLEAN;
301     }
302     else {
303       myReturnType = expressionType;
304     }
305
306     PsiElement container = PsiTreeUtil.getParentOfType(myElements[0], PsiClass.class, PsiMethod.class);
307     if (container instanceof PsiMethod) {
308       PsiElement[] elements = myElements;
309       if (myExpression == null) {
310         if (myOutputVariable != null) {
311           elements = ArrayUtil.append(myElements, myOutputVariable, PsiElement.class);
312         }
313         if (myCodeFragmentMember != null && myReturnType == ((PsiMethod)myCodeFragmentMember).getReturnType()) {
314           elements = ArrayUtil.append(myElements, ((PsiMethod)myCodeFragmentMember).getReturnTypeElement(), PsiElement.class);
315         }
316       }
317       myTypeParameterList = RefactoringUtil.createTypeParameterListWithUsedTypeParameters(((PsiMethod)container).getTypeParameterList(), elements);
318     }
319     List<PsiClassType> exceptions = ExceptionUtil.getThrownCheckedExceptions(myElements);
320     myThrownExceptions = exceptions.toArray(new PsiClassType[exceptions.size()]);
321
322     if (container instanceof PsiMethod) {
323       checkLocalClasses((PsiMethod) container);
324     }
325     return true;
326   }
327
328   private boolean isNotNull(PsiVariable outputVariable) {
329     final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false);
330     final PsiCodeBlock block = myElementFactory.createCodeBlock();
331     for (PsiElement element : myElements) {
332       block.add(element);
333     }
334     final PsiIfStatement statementFromText = (PsiIfStatement)myElementFactory.createStatementFromText("if (" + outputVariable.getName() + " == null);", null);
335     block.add(statementFromText);
336
337     final StandardInstructionVisitor visitor = new StandardInstructionVisitor();
338     final RunnerResult rc = dfaRunner.analyzeMethod(block, visitor);
339     if (rc == RunnerResult.OK) {
340       if (dfaRunner.problemsDetected(visitor)) {
341         final Pair<Set<Instruction>,Set<Instruction>>
342           conditionalExpressions = dfaRunner.getConstConditionalExpressions();
343         final Set<Instruction> falseSet = conditionalExpressions.getSecond();
344         for (Instruction instruction : falseSet) {
345           if (instruction instanceof BranchingInstruction) {
346             if (((BranchingInstruction)instruction).getPsiAnchor().getText().equals(statementFromText.getCondition().getText())) {
347               return true;
348             }
349           }
350         }
351       }
352     }
353     return false;
354   }
355
356   protected boolean checkOutputVariablesCount() {
357     int outputCount = (myHasExpressionOutput ? 1 : 0) + (myGenerateConditionalExit ? 1 : 0) + myOutputVariables.length;
358     return outputCount > 1;
359   }
360
361   private void checkCanBeChainedConstructor() {
362     if (!(myCodeFragmentMember instanceof PsiMethod)) {
363       return;
364     }
365     final PsiMethod method = (PsiMethod)myCodeFragmentMember;
366     if (!method.isConstructor() || myReturnType != PsiType.VOID) {
367       return;
368     }
369     final PsiCodeBlock body = method.getBody();
370     if (body == null) return;
371     final PsiStatement[] psiStatements = body.getStatements();
372     if (psiStatements.length > 0 && myElements [0] == psiStatements [0]) {
373       myCanBeChainedConstructor = true;
374     }
375   }
376
377   private void checkLocalClasses(final PsiMethod container) throws PrepareFailedException {
378     final List<PsiClass> localClasses = new ArrayList<PsiClass>();
379     container.accept(new JavaRecursiveElementWalkingVisitor() {
380       @Override public void visitClass(final PsiClass aClass) {
381         localClasses.add(aClass);
382       }
383
384       @Override public void visitAnonymousClass(final PsiAnonymousClass aClass) {
385         visitElement(aClass);
386       }
387
388       @Override public void visitTypeParameter(final PsiTypeParameter classParameter) {
389         visitElement(classParameter);
390       }
391     });
392     for(PsiClass localClass: localClasses) {
393       final boolean classExtracted = isExtractedElement(localClass);
394       final List<PsiElement> extractedReferences = Collections.synchronizedList(new ArrayList<PsiElement>());
395       final List<PsiElement> remainingReferences = Collections.synchronizedList(new ArrayList<PsiElement>());
396       ReferencesSearch.search(localClass).forEach(new Processor<PsiReference>() {
397         public boolean process(final PsiReference psiReference) {
398           final PsiElement element = psiReference.getElement();
399           final boolean elementExtracted = isExtractedElement(element);
400           if (elementExtracted && !classExtracted) {
401             extractedReferences.add(element);
402             return false;
403           }
404           if (!elementExtracted && classExtracted) {
405             remainingReferences.add(element);
406             return false;
407           }
408           return true;
409         }
410       });
411       if (!extractedReferences.isEmpty()) {
412         throw new PrepareFailedException("Cannot extract method because the selected code fragment uses local classes defined outside of the fragment", extractedReferences.get(0));
413       }
414       if (!remainingReferences.isEmpty()) {
415         throw new PrepareFailedException("Cannot extract method because the selected code fragment defines local classes used outside of the fragment", remainingReferences.get(0));
416       }
417       if (classExtracted) {
418         for (PsiVariable variable : myControlFlowWrapper.getUsedVariables()) {
419           if (isDeclaredInside(variable) && !variable.equals(myOutputVariable) && PsiUtil.resolveClassInType(variable.getType()) == localClass) {
420             throw new PrepareFailedException("Cannot extract method because the selected code fragment defines variable of local class type used outside of the fragment", variable);
421           }
422         }
423       }
424     }
425   }
426
427   private boolean isExtractedElement(final PsiElement element) {
428     boolean isExtracted = false;
429     for(PsiElement psiElement: myElements) {
430       if (PsiTreeUtil.isAncestor(psiElement, element, false)) {
431         isExtracted = true;
432         break;
433       }
434     }
435     return isExtracted;
436   }
437
438
439
440   private boolean shouldBeStatic() {
441     for(PsiElement element: myElements) {
442       final PsiExpressionStatement statement = PsiTreeUtil.getParentOfType(element, PsiExpressionStatement.class);
443       if (statement != null && RefactoringUtil.isSuperOrThisCall(statement, true, true)) {
444         return true;
445       }
446     }
447     PsiElement codeFragmentMember = myCodeFragmentMember;
448     while (codeFragmentMember != null && PsiTreeUtil.isAncestor(myTargetClass, codeFragmentMember, true)) {
449       if (codeFragmentMember instanceof PsiModifierListOwner && ((PsiModifierListOwner)codeFragmentMember).hasModifierProperty(PsiModifier.STATIC)) {
450         return true;
451       }
452       codeFragmentMember = PsiTreeUtil.getParentOfType(codeFragmentMember, PsiModifierListOwner.class, true);
453     }
454     return false;
455   }
456
457   public boolean showDialog(final boolean direct) {
458     AbstractExtractDialog dialog = createExtractMethodDialog(direct);
459     dialog.show();
460     if (!dialog.isOK()) return false;
461     apply(dialog);
462     return true;
463   }
464
465   protected void apply(final AbstractExtractDialog dialog) {
466     myMethodName = dialog.getChosenMethodName();
467     myVariableDatum = dialog.getChosenParameters();
468     myStatic |= dialog.isMakeStatic();
469     myIsChainedConstructor = dialog.isChainedConstructor();
470     myMethodVisibility = dialog.getVisibility();
471   }
472
473   protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) {
474     return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, myReturnType, myTypeParameterList,
475                                                          myThrownExceptions, myStatic, myCanBeStatic, myCanBeChainedConstructor, myInitialMethodName,
476                                                          myRefactoringName, myHelpId, myElements) {
477       protected boolean areTypesDirected() {
478         return direct;
479       }
480
481       @Override
482       protected boolean isOutputVariable(PsiVariable var) {
483         return ExtractMethodProcessor.this.isOutputVariable(var);
484       }
485     };
486   }
487
488   public boolean isOutputVariable(PsiVariable var) {
489     return ArrayUtil.find(myOutputVariables, var) != -1;
490   }
491
492   public boolean showDialog() {
493     return showDialog(true);
494   }
495
496   @TestOnly
497   public void testRun() throws IncorrectOperationException {
498     testPrepare();
499
500     ExtractMethodHandler.run(myProject, myEditor, this);
501   }
502
503   @TestOnly
504   public void testPrepare() {
505     myInputVariables.setFoldingAvailable(myInputVariables.isFoldingSelectedByDefault());
506     myMethodName = myInitialMethodName;
507     myVariableDatum = new ParameterTablePanel.VariableData[myInputVariables.getInputVariables().size()];
508     for (int i = 0; i < myInputVariables.getInputVariables().size(); i++) {
509       myVariableDatum[i] = myInputVariables.getInputVariables().get(i);
510     }
511   }
512
513   /**
514    * Invoked in command and in atomic action
515    */
516   public void doRefactoring() throws IncorrectOperationException {
517     List<PsiElement> elements = new ArrayList<PsiElement>();
518     for (PsiElement element : myElements) {
519       if (!(element instanceof PsiWhiteSpace || element instanceof PsiComment)) {
520         elements.add(element);
521       }
522     }
523     if (myExpression != null) {
524       myDuplicatesFinder = new DuplicatesFinder(PsiUtilCore.toPsiElementArray(elements), myInputVariables.copy(),
525                                                 new ArrayList<PsiVariable>());
526       myDuplicates = myDuplicatesFinder.findDuplicates(myTargetClass);
527     }
528     else if (elements.size() > 0){
529       myDuplicatesFinder = new DuplicatesFinder(PsiUtilCore.toPsiElementArray(elements), myInputVariables.copy(),
530                                                 myOutputVariable != null ? new VariableReturnValue(myOutputVariable) : null,
531                                                 Arrays.asList(myOutputVariables));
532       myDuplicates = myDuplicatesFinder.findDuplicates(myTargetClass);
533     } else {
534       myDuplicates = new ArrayList<Match>();
535     }
536
537     chooseAnchor();
538
539     int col = myEditor.getCaretModel().getLogicalPosition().column;
540     int line = myEditor.getCaretModel().getLogicalPosition().line;
541     LogicalPosition pos = new LogicalPosition(0, 0);
542     myEditor.getCaretModel().moveToLogicalPosition(pos);
543
544     final SearchScope processConflictsScope = myMethodVisibility.equals(PsiModifier.PRIVATE) ?
545                                         new LocalSearchScope(myTargetClass) :
546                                         GlobalSearchScope.projectScope(myProject);
547
548     final Map<PsiMethodCallExpression, PsiMethod> overloadsResolveMap = new HashMap<PsiMethodCallExpression, PsiMethod>();
549     final Runnable collectOverloads = new Runnable() {
550       public void run() {
551         ApplicationManager.getApplication().runReadAction(new Runnable() {
552           public void run() {
553             Map<PsiMethodCallExpression, PsiMethod> overloads =
554               ExtractMethodUtil.encodeOverloadTargets(myTargetClass, processConflictsScope, myMethodName, myCodeFragmentMember);
555             overloadsResolveMap.putAll(overloads);
556           }
557         });
558       }
559     };
560     final Runnable extract = new Runnable() {
561       public void run() {
562         doExtract();
563         ExtractMethodUtil.decodeOverloadTargets(overloadsResolveMap, myExtractedMethod, myCodeFragmentMember);
564       }
565     };
566     if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
567       collectOverloads.run();
568       extract.run();
569     } else {
570       if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(collectOverloads, "Collect overloads...", true, myProject)) return;
571       ApplicationManager.getApplication().runWriteAction(extract);
572     }
573
574     LogicalPosition pos1 = new LogicalPosition(line, col);
575     myEditor.getCaretModel().moveToLogicalPosition(pos1);
576     int offset = myMethodCall.getMethodExpression().getTextRange().getStartOffset();
577     myEditor.getCaretModel().moveToOffset(offset);
578     myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
579     myEditor.getSelectionModel().removeSelection();
580   }
581
582   private void doExtract() throws IncorrectOperationException {
583
584     PsiMethod newMethod = generateEmptyMethod(myThrownExceptions, myStatic);
585
586     myExpression = myInputVariables.replaceWrappedReferences(myElements, myExpression);
587     renameInputVariables();
588
589     LOG.assertTrue(myElements[0].isValid());
590
591     PsiCodeBlock body = newMethod.getBody();
592     myMethodCall = generateMethodCall(null, true);
593
594     LOG.assertTrue(myElements[0].isValid());
595
596     if (myExpression == null) {
597       String outVariableName = myOutputVariable != null ? getNewVariableName(myOutputVariable) : null;
598       PsiReturnStatement returnStatement;
599       if (myNullConditionalCheck) {
600         returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return null;", null);
601       } else if (myOutputVariable != null) {
602         returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return " + outVariableName + ";", null);
603       }
604       else if (myGenerateConditionalExit) {
605         returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return true;", null);
606       }
607       else {
608         returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return;", null);
609       }
610
611       boolean hasNormalExit = false;
612       PsiElement lastElement = myElements[myElements.length - 1];
613       if (!(lastElement instanceof PsiReturnStatement || lastElement instanceof PsiBreakStatement ||
614             lastElement instanceof PsiContinueStatement)) {
615         hasNormalExit = true;
616       }
617
618       PsiStatement exitStatementCopy = myControlFlowWrapper.getExitStatementCopy(returnStatement, myElements);
619
620
621       declareNecessaryVariablesInsideBody(body);
622
623
624
625       if (myNeedChangeContext) {
626         for (PsiElement element : myElements) {
627           ChangeContextUtil.encodeContextInfo(element, false);
628         }
629       }
630
631       body.addRange(myElements[0], myElements[myElements.length - 1]);
632       if (myNullConditionalCheck) {
633         body.add(myElementFactory.createStatementFromText("return " + myOutputVariable.getName() + ";", null));
634       } else if (myGenerateConditionalExit) {
635         body.add(myElementFactory.createStatementFromText("return false;", null));
636       }
637       else if (!myHasReturnStatement && hasNormalExit && myOutputVariable != null) {
638         final PsiReturnStatement insertedReturnStatement = (PsiReturnStatement)body.add(returnStatement);
639         if (myOutputVariables.length == 1) {
640           final PsiExpression returnValue = insertedReturnStatement.getReturnValue();
641           if (returnValue instanceof PsiReferenceExpression) {
642             final PsiElement resolved = ((PsiReferenceExpression)returnValue).resolve();
643             if (resolved instanceof PsiLocalVariable && Comparing.strEqual(((PsiVariable)resolved).getName(), outVariableName)) {
644               final PsiStatement statement = PsiTreeUtil.getPrevSiblingOfType(insertedReturnStatement, PsiStatement.class);
645               if (statement instanceof PsiDeclarationStatement) {
646                 final PsiElement[] declaredElements = ((PsiDeclarationStatement)statement).getDeclaredElements();
647                 if (ArrayUtil.find(declaredElements, resolved) != -1) {
648                   InlineUtil.inlineVariable((PsiVariable)resolved, ((PsiVariable)resolved).getInitializer(), (PsiReferenceExpression)returnValue);
649                   resolved.delete();
650                 }
651               }
652             }
653           }
654         }
655       }
656       if (myNullConditionalCheck) {
657         final String varName = myOutputVariable.getName();
658         if (isDeclaredInside(myOutputVariable)) {
659           declareVariableAtMethodCallLocation(varName);
660         }
661         else {
662           PsiExpressionStatement assignmentExpression =
663             (PsiExpressionStatement)myElementFactory.createStatementFromText(varName + "=x;", null);
664           assignmentExpression = (PsiExpressionStatement)addToMethodCallLocation(assignmentExpression);
665           myMethodCall =
666             (PsiMethodCallExpression)((PsiAssignmentExpression)assignmentExpression.getExpression()).getRExpression().replace(myMethodCall);
667         }
668         declareNecessaryVariablesAfterCall(myOutputVariable);
669         PsiIfStatement ifStatement =
670           (PsiIfStatement)myElementFactory.createStatementFromText(myHasReturnStatementOutput || (myGenerateConditionalExit && myFirstExitStatementCopy instanceof PsiReturnStatement &&
671                                                                                                   ((PsiReturnStatement)myFirstExitStatementCopy).getReturnValue() != null)
672                                                                    ? "if (" + varName + "==null) return null;"
673                                                                    : "if (" + varName + "==null) return;", null);
674         ifStatement = (PsiIfStatement)addToMethodCallLocation(ifStatement);
675         CodeStyleManager.getInstance(myProject).reformat(ifStatement);
676       }
677       else if (myGenerateConditionalExit) {
678         PsiIfStatement ifStatement = (PsiIfStatement)myElementFactory.createStatementFromText("if (a) b;", null);
679         ifStatement = (PsiIfStatement)addToMethodCallLocation(ifStatement);
680         myMethodCall = (PsiMethodCallExpression)ifStatement.getCondition().replace(myMethodCall);
681         ifStatement.getThenBranch().replace(myFirstExitStatementCopy);
682         CodeStyleManager.getInstance(myProject).reformat(ifStatement);
683       }
684       else if (myOutputVariable != null) {
685         String name = myOutputVariable.getName();
686         boolean toDeclare = isDeclaredInside(myOutputVariable);
687         if (!toDeclare) {
688           PsiExpressionStatement statement = (PsiExpressionStatement)myElementFactory.createStatementFromText(name + "=x;", null);
689           statement = (PsiExpressionStatement)myStyleManager.reformat(statement);
690           statement = (PsiExpressionStatement)addToMethodCallLocation(statement);
691           PsiAssignmentExpression assignment = (PsiAssignmentExpression)statement.getExpression();
692           myMethodCall = (PsiMethodCallExpression)assignment.getRExpression().replace(myMethodCall);
693         }
694         else {
695           declareVariableAtMethodCallLocation(name);
696         }
697       }
698       else if (myHasReturnStatementOutput) {
699         PsiStatement statement = myElementFactory.createStatementFromText("return x;", null);
700         statement = (PsiStatement)addToMethodCallLocation(statement);
701         myMethodCall = (PsiMethodCallExpression)((PsiReturnStatement)statement).getReturnValue().replace(myMethodCall);
702       }
703       else {
704         PsiStatement statement = myElementFactory.createStatementFromText("x();", null);
705         statement = (PsiStatement)addToMethodCallLocation(statement);
706         myMethodCall = (PsiMethodCallExpression)((PsiExpressionStatement)statement).getExpression().replace(myMethodCall);
707       }
708       if (myHasReturnStatement && !myHasReturnStatementOutput && !hasNormalExit) {
709         PsiStatement statement = myElementFactory.createStatementFromText("return;", null);
710         addToMethodCallLocation(statement);
711       }
712       else if (!myGenerateConditionalExit && exitStatementCopy != null) {
713         addToMethodCallLocation(exitStatementCopy);
714       }
715
716       if (!myNullConditionalCheck) {
717         declareNecessaryVariablesAfterCall(myOutputVariable);
718       }
719
720       deleteExtracted();
721     }
722     else {
723       declareNecessaryVariablesInsideBody(body);
724       if (myHasExpressionOutput) {
725         PsiReturnStatement returnStatement = (PsiReturnStatement)myElementFactory.createStatementFromText("return x;", null);
726         final PsiExpression returnValue = RefactoringUtil.convertInitializerToNormalExpression(myExpression, myForcedReturnType);
727         returnStatement.getReturnValue().replace(returnValue);
728         body.add(returnStatement);
729       }
730       else {
731         PsiExpressionStatement statement = (PsiExpressionStatement)myElementFactory.createStatementFromText("x;", null);
732         statement.getExpression().replace(myExpression);
733         body.add(statement);
734       }
735       PsiExpression expression2Replace = myExpression;
736       if (myExpression instanceof PsiAssignmentExpression) {
737         expression2Replace = ((PsiAssignmentExpression)myExpression).getRExpression();
738       } else if (myExpression instanceof PsiPostfixExpression || myExpression instanceof PsiPrefixExpression) {
739         final IElementType elementType = myExpression instanceof PsiPostfixExpression
740                                           ? ((PsiPostfixExpression)myExpression).getOperationTokenType()
741                                           : ((PsiPrefixExpression)myExpression).getOperationTokenType();
742         if (elementType == JavaTokenType.PLUSPLUS || elementType == JavaTokenType.MINUSMINUS) {
743           PsiExpression operand = myExpression instanceof PsiPostfixExpression ? ((PsiPostfixExpression)myExpression).getOperand() :
744                                   ((PsiPrefixExpression)myExpression).getOperand();
745           expression2Replace =
746             ((PsiBinaryExpression)myExpression.replace(myElementFactory.createExpressionFromText(operand.getText() + " + x", operand))).getROperand();
747         }
748
749       }
750       myExpression = (PsiExpression)IntroduceVariableBase.replace(expression2Replace, myMethodCall, myProject);
751       myMethodCall = PsiTreeUtil.getParentOfType(myExpression.findElementAt(myExpression.getText().indexOf(myMethodCall.getText())), PsiMethodCallExpression.class);
752       declareNecessaryVariablesAfterCall(myOutputVariable);
753     }
754
755     if (myAnchor instanceof PsiField) {
756       ((PsiField)myAnchor).normalizeDeclaration();
757     }
758
759     adjustFinalParameters(newMethod);
760
761     for (int i = 0, length = myVariableDatum.length; i < length; i++) {
762       ParameterTablePanel.VariableData data = myVariableDatum[i];
763       final PsiVariable variable = data.variable;
764       final PsiParameter psiParameter = newMethod.getParameterList().getParameters()[i];
765       if (!TypeConversionUtil.isAssignable(variable.getType(), psiParameter.getType())) {
766         for (PsiReference reference : ReferencesSearch.search(psiParameter, new LocalSearchScope(body))){
767           final PsiElement element = reference.getElement();
768           if (element != null) {
769             final PsiElement parent = element.getParent();
770             if (parent instanceof PsiTypeCastExpression) {
771               RedundantCastUtil.removeCast((PsiTypeCastExpression)parent);
772             }
773           }
774         }
775       }
776     }
777
778     myExtractedMethod = (PsiMethod)myTargetClass.addAfter(newMethod, myAnchor);
779     if (isNeedToChangeCallContext() && myNeedChangeContext) {
780       ChangeContextUtil.decodeContextInfo(myExtractedMethod, myTargetClass, RefactoringUtil.createThisExpression(myManager, null));
781       if (myMethodCall.resolveMethod() != myExtractedMethod) {
782         final PsiReferenceExpression methodExpression = myMethodCall.getMethodExpression();
783         methodExpression.setQualifierExpression(RefactoringUtil.createThisExpression(myManager, myTargetClass));
784       }
785     }
786
787   }
788
789   protected boolean isNeedToChangeCallContext() {
790     return true;
791   }
792
793   private void declareVariableAtMethodCallLocation(String name) {
794     PsiDeclarationStatement statement =
795       myElementFactory.createVariableDeclarationStatement(name, myOutputVariable.getType(), myMethodCall);
796     statement = (PsiDeclarationStatement)addToMethodCallLocation(statement);
797     PsiVariable var = (PsiVariable)statement.getDeclaredElements()[0];
798     myMethodCall = (PsiMethodCallExpression)var.getInitializer();
799     var.getModifierList().replace(myOutputVariable.getModifierList());
800   }
801
802   private void adjustFinalParameters(final PsiMethod method) throws IncorrectOperationException {
803     final IncorrectOperationException[] exc = new IncorrectOperationException[1];
804     exc[0] = null;
805     final PsiParameter[] parameters = method.getParameterList().getParameters();
806     if (parameters.length > 0) {
807       if (CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS) {
808         method.accept(new JavaRecursiveElementVisitor() {
809
810           @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
811             final PsiElement resolved = expression.resolve();
812             if (resolved != null) {
813               final int index = ArrayUtil.find(parameters, resolved);
814               if (index >= 0) {
815                 final PsiParameter param = parameters[index];
816                 if (param.hasModifierProperty(PsiModifier.FINAL) && PsiUtil.isAccessedForWriting(expression)) {
817                   try {
818                     PsiUtil.setModifierProperty(param, PsiModifier.FINAL, false);
819                   }
820                   catch (IncorrectOperationException e) {
821                     exc[0] = e;
822                   }
823                 }
824               }
825             }
826             super.visitReferenceExpression(expression);
827           }
828         });
829       }
830       else {
831         method.accept(new JavaRecursiveElementVisitor() {
832           @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
833             final PsiElement resolved = expression.resolve();
834             final int index = ArrayUtil.find(parameters, resolved);
835             if (index >= 0) {
836               final PsiParameter param = parameters[index];
837               if (!param.hasModifierProperty(PsiModifier.FINAL) && RefactoringUtil.isInsideAnonymous(expression, method)) {
838                 try {
839                   PsiUtil.setModifierProperty(param, PsiModifier.FINAL, true);
840                 }
841                 catch (IncorrectOperationException e) {
842                   exc[0] = e;
843                 }
844               }
845             }
846             super.visitReferenceExpression(expression);
847           }
848         });
849       }
850       if (exc[0] != null) {
851         throw exc[0];
852       }
853     }
854   }
855
856   public List<Match> getDuplicates() {
857     if (myIsChainedConstructor) {
858       return filterChainedConstructorDuplicates(myDuplicates);
859     }
860     return myDuplicates;
861   }
862
863   private static List<Match> filterChainedConstructorDuplicates(final List<Match> duplicates) {
864     List<Match> result = new ArrayList<Match>();
865     for(Match duplicate: duplicates) {
866       final PsiElement matchStart = duplicate.getMatchStart();
867       final PsiMethod method = PsiTreeUtil.getParentOfType(matchStart, PsiMethod.class);
868       if (method != null && method.isConstructor()) {
869         final PsiCodeBlock body = method.getBody();
870         if (body != null) {
871           final PsiStatement[] psiStatements = body.getStatements();
872           if (psiStatements.length > 0 && matchStart == psiStatements [0]) {
873             result.add(duplicate);
874           }
875         }
876       }
877     }
878     return result;
879   }
880
881   public PsiElement processMatch(Match match) throws IncorrectOperationException {
882     match.changeSignature(myExtractedMethod);
883     if (RefactoringUtil.isInStaticContext(match.getMatchStart(), myExtractedMethod.getContainingClass())) {
884       PsiUtil.setModifierProperty(myExtractedMethod, PsiModifier.STATIC, true);
885     }
886     final PsiMethodCallExpression methodCallExpression = generateMethodCall(match.getInstanceExpression(), false);
887
888     ArrayList<ParameterTablePanel.VariableData> datas = new ArrayList<ParameterTablePanel.VariableData>();
889     for (final ParameterTablePanel.VariableData variableData : myVariableDatum) {
890       if (variableData.passAsParameter) {
891         datas.add(variableData);
892       }
893     }
894     for (ParameterTablePanel.VariableData data : datas) {
895       final List<PsiElement> parameterValue = match.getParameterValues(data.variable);
896       if (parameterValue != null) {
897         for (PsiElement val : parameterValue) {
898           methodCallExpression.getArgumentList().add(val);
899         }
900       }
901     }
902     return match.replace(myExtractedMethod, methodCallExpression, myOutputVariable);
903   }
904
905   private void deleteExtracted() throws IncorrectOperationException {
906     if (myEnclosingBlockStatement == null) {
907       myElements[0].getParent().deleteChildRange(myElements[0], myElements[myElements.length - 1]);
908     }
909     else {
910       myEnclosingBlockStatement.delete();
911     }
912   }
913
914   protected PsiElement addToMethodCallLocation(PsiStatement statement) throws IncorrectOperationException {
915     if (myEnclosingBlockStatement == null) {
916       PsiElement containingStatement = myElements[0] instanceof PsiComment ? myElements[0] : PsiTreeUtil.getParentOfType(myExpression != null ? myExpression : myElements[0], PsiStatement.class, false);
917       if (containingStatement == null) {
918         containingStatement = PsiTreeUtil.getParentOfType(myExpression != null ? myExpression : myElements[0], PsiComment.class, false);
919       }
920
921       return containingStatement.getParent().addBefore(statement, containingStatement);
922     }
923     else {
924       return myEnclosingBlockStatement.getParent().addBefore(statement, myEnclosingBlockStatement);
925     }
926   }
927
928   private void renameInputVariables() throws IncorrectOperationException {
929     for (ParameterTablePanel.VariableData data : myVariableDatum) {
930       PsiVariable variable = data.variable;
931       if (!data.name.equals(variable.getName())) {
932         for (PsiElement element : myElements) {
933           RefactoringUtil.renameVariableReferences(variable, data.name, new LocalSearchScope(element));
934         }
935       }
936     }
937   }
938
939   public PsiClass getTargetClass() {
940     return myTargetClass;
941   }
942
943   private PsiMethod generateEmptyMethod(PsiClassType[] exceptions, boolean isStatic) throws IncorrectOperationException {
944     PsiMethod newMethod;
945     if (myIsChainedConstructor) {
946       newMethod = myElementFactory.createConstructor();      
947     }
948     else {
949       newMethod = myElementFactory.createMethod(myMethodName, myReturnType);
950       PsiUtil.setModifierProperty(newMethod, PsiModifier.STATIC, isStatic);
951     }
952     PsiUtil.setModifierProperty(newMethod, myMethodVisibility, true);
953     if (myTypeParameterList != null) {
954       newMethod.getTypeParameterList().replace(myTypeParameterList);
955     }
956     PsiCodeBlock body = newMethod.getBody();
957     LOG.assertTrue(body != null);
958
959     boolean isFinal = CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS;
960     PsiParameterList list = newMethod.getParameterList();
961     for (ParameterTablePanel.VariableData data : myVariableDatum) {
962       if (data.passAsParameter) {
963         PsiParameter parm = myElementFactory.createParameter(data.name, data.type);
964         if (isFinal) {
965           PsiUtil.setModifierProperty(parm, PsiModifier.FINAL, true);
966         }
967         list.add(parm);
968       }
969       else {
970         @NonNls StringBuilder buffer = new StringBuilder();
971         if (isFinal) {
972           buffer.append("final ");
973         }
974         buffer.append("int ");
975         buffer.append(data.name);
976         buffer.append("=;");
977         String text = buffer.toString();
978
979         PsiDeclarationStatement declaration = (PsiDeclarationStatement)myElementFactory.createStatementFromText(text, null);
980         declaration = (PsiDeclarationStatement)myStyleManager.reformat(declaration);
981         final PsiTypeElement typeElement = myElementFactory.createTypeElement(data.type);
982         ((PsiVariable)declaration.getDeclaredElements()[0]).getTypeElement().replace(typeElement);
983         body.add(declaration);
984       }
985     }
986
987     PsiReferenceList throwsList = newMethod.getThrowsList();
988     for (PsiClassType exception : exceptions) {
989       throwsList.add(JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createReferenceElementByType(exception));
990     }
991
992     if (myNullConditionalCheck) {
993       final boolean isNullCheckReturnNull = (myHasExpressionOutput ? 1 : 0) + (myGenerateConditionalExit ? 1 : 0) + myOutputVariables.length <= 1;
994       if (isNullCheckReturnNull && PsiUtil.isLanguageLevel5OrHigher(myElements[0])) {
995         final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject);
996         final PsiClass nullableAnnotationClass =
997           JavaPsiFacade.getInstance(myProject).findClass(manager.getDefaultNullable(), GlobalSearchScope.allScope(myProject));
998         if (nullableAnnotationClass != null) {
999           new AddNullableAnnotationFix(newMethod).invoke(myProject, myEditor, myTargetClass.getContainingFile());
1000         }
1001       }
1002     }
1003
1004     return (PsiMethod)myStyleManager.reformat(newMethod);
1005   }
1006
1007   @NotNull
1008   protected PsiMethodCallExpression generateMethodCall(PsiExpression instanceQualifier, final boolean generateArgs) throws IncorrectOperationException {
1009     @NonNls StringBuilder buffer = new StringBuilder();
1010
1011     final boolean skipInstanceQualifier;
1012     if (myIsChainedConstructor) {
1013       skipInstanceQualifier = true;
1014       buffer.append(PsiKeyword.THIS);
1015     }
1016     else {
1017       skipInstanceQualifier = instanceQualifier == null || instanceQualifier instanceof PsiThisExpression;
1018       if (skipInstanceQualifier) {
1019         if (myNeedChangeContext) {
1020           boolean needsThisQualifier = false;
1021           PsiElement parent = myCodeFragmentMember;
1022           while (!myTargetClass.equals(parent)) {
1023             if (parent instanceof PsiMethod) {
1024               String methodName = ((PsiMethod)parent).getName();
1025               if (methodName.equals(myMethodName)) {
1026                 needsThisQualifier = true;
1027                 break;
1028               }
1029             }
1030             parent = parent.getParent();
1031           }
1032           if (needsThisQualifier) {
1033             buffer.append(myTargetClass.getName());
1034             buffer.append(".this.");
1035           }
1036         }
1037       }
1038       else {
1039         buffer.append("qqq.");
1040       }
1041
1042       buffer.append(myMethodName);
1043     }
1044     buffer.append("(");
1045     if (generateArgs) {
1046       int count = 0;
1047       for (ParameterTablePanel.VariableData data : myVariableDatum) {
1048         if (data.passAsParameter) {
1049           if (count > 0) {
1050             buffer.append(",");
1051           }
1052           myInputVariables.appendCallArguments(data, buffer);
1053           count++;
1054         }
1055       }
1056     }
1057     buffer.append(")");
1058     String text = buffer.toString();
1059
1060     PsiMethodCallExpression expr = (PsiMethodCallExpression)myElementFactory.createExpressionFromText(text, null);
1061     expr = (PsiMethodCallExpression)myStyleManager.reformat(expr);
1062     if (!skipInstanceQualifier) {
1063       PsiExpression qualifierExpression = expr.getMethodExpression().getQualifierExpression();
1064       LOG.assertTrue(qualifierExpression != null);
1065       qualifierExpression.replace(instanceQualifier);
1066     }
1067     return (PsiMethodCallExpression)JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(expr);
1068   }
1069
1070   private boolean chooseTargetClass(PsiElement codeFragment, final Pass<ExtractMethodProcessor> extractPass) throws PrepareFailedException {
1071     final List<PsiVariable> inputVariables = myControlFlowWrapper.getInputVariables(codeFragment);
1072
1073     myNeedChangeContext = false;
1074     myTargetClass = myCodeFragmentMember instanceof PsiMember
1075                     ? ((PsiMember)myCodeFragmentMember).getContainingClass()
1076                     : PsiTreeUtil.getParentOfType(myCodeFragmentMember, PsiClass.class);
1077     if (!shouldAcceptCurrentTarget(extractPass, myTargetClass)) {
1078
1079       final LinkedHashMap<PsiClass, List<PsiVariable>> classes = new LinkedHashMap<PsiClass, List<PsiVariable>>();
1080       final PsiElementProcessor<PsiClass> processor = new PsiElementProcessor<PsiClass>() {
1081         @Override
1082         public boolean execute(@NotNull PsiClass selectedClass) {
1083           final List<PsiVariable> array = classes.get(selectedClass);
1084           myNeedChangeContext = myTargetClass != selectedClass;
1085           myTargetClass = selectedClass;
1086           if (array != null) {
1087             for (PsiVariable variable : array) {
1088               if (!inputVariables.contains(variable)) {
1089                 inputVariables.addAll(array);
1090               }
1091             }
1092           }
1093           try {
1094             return applyChosenClassAndExtract(inputVariables, extractPass);
1095           }
1096           catch (PrepareFailedException e) {
1097             if (myShowErrorDialogs) {
1098               CommonRefactoringUtil
1099                 .showErrorHint(myProject, myEditor, e.getMessage(), ExtractMethodHandler.REFACTORING_NAME, HelpID.EXTRACT_METHOD);
1100               ExtractMethodHandler.highlightPrepareError(e, e.getFile(), myEditor, myProject);
1101             }
1102             return false;
1103           }
1104         }
1105       };
1106
1107       classes.put(myTargetClass, null);
1108       PsiElement target = myTargetClass.getParent();
1109       PsiElement targetMember = myTargetClass;
1110       while (true) {
1111         if (target instanceof PsiFile) break;
1112         if (target instanceof PsiClass) {
1113           boolean success = true;
1114           final List<PsiVariable> array = new ArrayList<PsiVariable>();
1115           for (PsiElement el : myElements) {
1116             if (!ControlFlowUtil.collectOuterLocals(array, el, myCodeFragmentMember, targetMember)) {
1117               success = false;
1118               break;
1119             }
1120           }
1121           if (success) {
1122             classes.put((PsiClass)target, array);
1123             if (shouldAcceptCurrentTarget(extractPass, target)) {
1124               return processor.execute((PsiClass)target);
1125             }
1126           }
1127         }
1128         targetMember = target;
1129         target = target.getParent();
1130       }
1131
1132       if (classes.size() > 1) {
1133         final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]);
1134         NavigationUtil.getPsiElementPopup(psiClasses, new PsiClassListCellRenderer(), "Choose Destination Class", processor).showInBestPositionFor(myEditor);
1135         return true;
1136       }
1137     }
1138
1139     return applyChosenClassAndExtract(inputVariables, extractPass);
1140   }
1141
1142   private void declareNecessaryVariablesInsideBody(PsiCodeBlock body) throws IncorrectOperationException {
1143     List<PsiVariable> usedVariables = myControlFlowWrapper.getUsedVariablesInBody();
1144     for (PsiVariable variable : usedVariables) {
1145       boolean toDeclare = !isDeclaredInside(variable) && myInputVariables.toDeclareInsideBody(variable);
1146       if (toDeclare) {
1147         String name = variable.getName();
1148         PsiDeclarationStatement statement = myElementFactory.createVariableDeclarationStatement(name, variable.getType(), null);
1149         body.add(statement);
1150       }
1151     }
1152   }
1153
1154   protected void declareNecessaryVariablesAfterCall(PsiVariable outputVariable) throws IncorrectOperationException {
1155     List<PsiVariable> usedVariables = myControlFlowWrapper.getUsedVariables();
1156     Collection<ControlFlowUtil.VariableInfo> reassigned = myControlFlowWrapper.getInitializedTwice();
1157     for (PsiVariable variable : usedVariables) {
1158       boolean toDeclare = isDeclaredInside(variable) && !variable.equals(outputVariable);
1159       if (toDeclare) {
1160         String name = variable.getName();
1161         PsiDeclarationStatement statement = myElementFactory.createVariableDeclarationStatement(name, variable.getType(), null);
1162         if (reassigned.contains(new ControlFlowUtil.VariableInfo(variable, null))) {
1163           final PsiElement[] psiElements = statement.getDeclaredElements();
1164           assert psiElements.length > 0;
1165           PsiVariable var = (PsiVariable) psiElements [0];
1166           PsiUtil.setModifierProperty(var, PsiModifier.FINAL, false);
1167         }
1168         addToMethodCallLocation(statement);
1169       }
1170     }
1171   }
1172
1173   public PsiMethodCallExpression getMethodCall() {
1174     return myMethodCall;
1175   }
1176
1177   public void setMethodCall(PsiMethodCallExpression methodCall) {
1178     myMethodCall = methodCall;
1179   }
1180
1181   public boolean isDeclaredInside(PsiVariable variable) {
1182     if (variable instanceof ImplicitVariable) return false;
1183     int startOffset = myElements[0].getTextRange().getStartOffset();
1184     int endOffset = myElements[myElements.length - 1].getTextRange().getEndOffset();
1185     PsiIdentifier nameIdentifier = variable.getNameIdentifier();
1186     if (nameIdentifier == null) return false;
1187     final TextRange range = nameIdentifier.getTextRange();
1188     if (range == null) return false;
1189     int offset = range.getStartOffset();
1190     return startOffset <= offset && offset <= endOffset;
1191   }
1192
1193   private String getNewVariableName(PsiVariable variable) {
1194     for (ParameterTablePanel.VariableData data : myVariableDatum) {
1195       if (data.variable.equals(variable)) {
1196         return data.name;
1197       }
1198     }
1199     return variable.getName();
1200   }
1201
1202   private static boolean shouldAcceptCurrentTarget(Pass<ExtractMethodProcessor> extractPass, PsiElement target) {
1203     return extractPass == null && !(target instanceof PsiAnonymousClass);
1204   }
1205
1206   private boolean applyChosenClassAndExtract(List<PsiVariable> inputVariables, @Nullable Pass<ExtractMethodProcessor> extractPass)
1207     throws PrepareFailedException {
1208     myStatic = shouldBeStatic();
1209     if (!PsiUtil.isLocalOrAnonymousClass(myTargetClass) && (myTargetClass.getContainingClass() == null || myTargetClass.hasModifierProperty(PsiModifier.STATIC))) {
1210       ElementNeedsThis needsThis = new ElementNeedsThis(myTargetClass);
1211       for (int i = 0; i < myElements.length && !needsThis.usesMembers(); i++) {
1212         PsiElement element = myElements[i];
1213         element.accept(needsThis);
1214       }
1215       myCanBeStatic = !needsThis.usesMembers();
1216     }
1217     else {
1218       myCanBeStatic = false;
1219     }
1220
1221     myInputVariables = new InputVariables(inputVariables, myProject, new LocalSearchScope(myElements), true);
1222
1223     if (!checkExitPoints()){
1224       return false;
1225     }
1226     if (extractPass != null) {
1227       extractPass.pass(this);
1228     }
1229     return true;
1230   }
1231
1232   private void chooseAnchor() {
1233     myAnchor = myCodeFragmentMember;
1234     while (!myAnchor.getParent().equals(myTargetClass)) {
1235       myAnchor = myAnchor.getParent();
1236     }
1237   }
1238
1239   private void showMultipleExitPointsMessage() {
1240     if (myShowErrorDialogs) {
1241       HighlightManager highlightManager = HighlightManager.getInstance(myProject);
1242       PsiStatement[] exitStatementsArray = myExitStatements.toArray(new PsiStatement[myExitStatements.size()]);
1243       EditorColorsManager manager = EditorColorsManager.getInstance();
1244       TextAttributes attributes = manager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
1245       highlightManager.addOccurrenceHighlights(myEditor, exitStatementsArray, attributes, true, null);
1246       String message = RefactoringBundle
1247         .getCannotRefactorMessage(RefactoringBundle.message("there.are.multiple.exit.points.in.the.selected.code.fragment"));
1248       CommonRefactoringUtil.showErrorHint(myProject, myEditor, message, myRefactoringName, myHelpId);
1249       WindowManager.getInstance().getStatusBar(myProject).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
1250     }
1251   }
1252
1253   private void showMultipleOutputMessage(PsiType expressionType) {
1254     if (myShowErrorDialogs) {
1255       StringBuilder buffer = new StringBuilder();
1256       buffer.append(RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("there.are.multiple.output.values.for.the.selected.code.fragment")));
1257       buffer.append("\n");
1258       if (myHasExpressionOutput) {
1259         buffer.append("    ").append(RefactoringBundle.message("expression.result")).append(": ");
1260         buffer.append(PsiFormatUtil.formatType(expressionType, 0, PsiSubstitutor.EMPTY));
1261         buffer.append(",\n");
1262       }
1263       if (myGenerateConditionalExit) {
1264         buffer.append("    ").append(RefactoringBundle.message("boolean.method.result"));
1265         buffer.append(",\n");
1266       }
1267       for (int i = 0; i < myOutputVariables.length; i++) {
1268         PsiVariable var = myOutputVariables[i];
1269         buffer.append("    ");
1270         buffer.append(var.getName());
1271         buffer.append(" : ");
1272         buffer.append(PsiFormatUtil.formatType(var.getType(), 0, PsiSubstitutor.EMPTY));
1273         if (i < myOutputVariables.length - 1) {
1274           buffer.append(",\n");
1275         }
1276         else {
1277           buffer.append(".");
1278         }
1279       }
1280       buffer.append("\nWould you like to Extract Method Object?");
1281
1282       String message = buffer.toString();
1283
1284       if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message);
1285       RefactoringMessageDialog dialog = new RefactoringMessageDialog(myRefactoringName, message, myHelpId, "OptionPane.errorIcon", true,
1286                                                                      myProject);
1287       dialog.show();
1288       if (dialog.isOK()) {
1289         new ExtractMethodObjectHandler().invoke(myProject, myEditor, myTargetClass.getContainingFile(), DataManager.getInstance().getDataContext());
1290       }
1291     }
1292   }
1293
1294   public PsiMethod getExtractedMethod() {
1295     return myExtractedMethod;
1296   }
1297
1298   public boolean hasDuplicates() {
1299     final List<Match> duplicates = getDuplicates();
1300     return duplicates != null && !duplicates.isEmpty();
1301   }
1302
1303   public boolean hasDuplicates(Set<VirtualFile> files) {
1304     if (hasDuplicates()) return true;
1305     final PsiManager psiManager = PsiManager.getInstance(myProject);
1306     for (VirtualFile file : files) {
1307       if (myDuplicatesFinder != null && !myDuplicatesFinder.findDuplicates(psiManager.findFile(file)).isEmpty()) return true;
1308     }
1309     return false;
1310   }
1311
1312   @Nullable
1313   public String getConfirmDuplicatePrompt(Match match) {
1314     final boolean needToBeStatic = RefactoringUtil.isInStaticContext(match.getMatchStart(), myExtractedMethod.getContainingClass());
1315     final String changedSignature = match.getChangedSignature(myExtractedMethod, needToBeStatic, VisibilityUtil.getVisibilityStringToDisplay(myExtractedMethod));
1316     if (changedSignature != null) {
1317       return RefactoringBundle.message("replace.this.code.fragment.and.change.signature", changedSignature);
1318     }
1319     if (needToBeStatic && !myExtractedMethod.hasModifierProperty(PsiModifier.STATIC)) {
1320       return RefactoringBundle.message("replace.this.code.fragment.and.make.method.static");
1321     }
1322     return null;
1323   }
1324
1325   @Override
1326   public String getReplaceDuplicatesTitle(int idx, int size) {
1327     return RefactoringBundle.message("process.duplicates.title", idx, size);
1328   }
1329
1330   public InputVariables getInputVariables() {
1331     return myInputVariables;
1332   }
1333 }