introduce constant: do not delete self if it is still needed
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / introduceField / BaseExpressionToFieldHandler.java
1 /*
2  * Created by IntelliJ IDEA.
3  * User: dsl
4  * Date: 29.05.2002
5  * Time: 13:05:34
6  * To change template for new class use
7  * Code Style | Class Templates options (Tools | IDE Options).
8  */
9 package com.intellij.refactoring.introduceField;
10
11 import com.intellij.codeInsight.AnnotationUtil;
12 import com.intellij.codeInsight.ChangeContextUtil;
13 import com.intellij.codeInsight.TestUtil;
14 import com.intellij.codeInsight.highlighting.HighlightManager;
15 import com.intellij.ide.util.DirectoryChooserUtil;
16 import com.intellij.ide.util.PackageUtil;
17 import com.intellij.openapi.application.ApplicationManager;
18 import com.intellij.openapi.command.CommandProcessor;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.editor.colors.EditorColors;
22 import com.intellij.openapi.editor.colors.EditorColorsManager;
23 import com.intellij.openapi.editor.markup.RangeHighlighter;
24 import com.intellij.openapi.editor.markup.TextAttributes;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.util.Pass;
27 import com.intellij.openapi.util.text.StringUtil;
28 import com.intellij.openapi.wm.WindowManager;
29 import com.intellij.psi.*;
30 import com.intellij.psi.codeStyle.CodeStyleManager;
31 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
32 import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
33 import com.intellij.psi.search.GlobalSearchScope;
34 import com.intellij.psi.util.PsiTreeUtil;
35 import com.intellij.psi.util.PsiUtil;
36 import com.intellij.refactoring.IntroduceHandlerBase;
37 import com.intellij.refactoring.RefactoringActionHandler;
38 import com.intellij.refactoring.RefactoringBundle;
39 import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
40 import com.intellij.refactoring.rename.RenameJavaVariableProcessor;
41 import com.intellij.refactoring.util.CommonRefactoringUtil;
42 import com.intellij.refactoring.util.EnumConstantsUtil;
43 import com.intellij.refactoring.util.RefactoringUtil;
44 import com.intellij.refactoring.util.occurences.OccurenceManager;
45 import com.intellij.util.IncorrectOperationException;
46 import org.jetbrains.annotations.NonNls;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
53
54 public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase implements RefactoringActionHandler {
55   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceField.BaseExpressionToFieldHandler");
56
57   public enum InitializationPlace {
58     IN_CURRENT_METHOD,
59     IN_FIELD_DECLARATION,
60     IN_CONSTRUCTOR,
61     IN_SETUP_METHOD
62   }
63
64   private PsiClass myParentClass;
65
66   protected boolean invokeImpl(final Project project, @NotNull final PsiExpression selectedExpr, final Editor editor) {
67     final PsiElement element = getPhysicalElement(selectedExpr);
68
69     final PsiFile file = element.getContainingFile();
70     LOG.assertTrue(file != null, "expr.getContainingFile() == null");
71
72     if (LOG.isDebugEnabled()) {
73       LOG.debug("expression:" + selectedExpr);
74     }
75
76     myParentClass = getParentClass(selectedExpr);
77     if (myParentClass == null) {
78       if (JspPsiUtil.isInJspFile(file)) {
79         CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.message("error.not.supported.for.jsp", getRefactoringName()),
80                                             getRefactoringName(), getHelpID());
81         return false;
82       }
83       else {
84         LOG.assertTrue(false);
85         return false;
86       }
87     }
88
89     if (!validClass(myParentClass, editor)) {
90       return false;
91     }
92
93     PsiType tempType = getTypeByExpression(selectedExpr);
94     if (tempType == null) {
95       String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("unknown.expression.type"));
96       CommonRefactoringUtil.showErrorHint(project, editor, message, getRefactoringName(), getHelpID());
97       return false;
98     }
99
100     if (PsiType.VOID.equals(tempType)) {
101       String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.expression.has.void.type"));
102       CommonRefactoringUtil.showErrorHint(project, editor, message, getRefactoringName(), getHelpID());
103       return false;
104     }
105
106
107     if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return false;
108
109     final PsiClass parentClass = myParentClass;
110     final OccurenceManager occurenceManager = createOccurenceManager(selectedExpr, parentClass);
111     final PsiExpression[] occurrences = occurenceManager.getOccurences();
112     final PsiElement anchorStatementIfAll = occurenceManager.getAnchorStatementForAll();
113
114     List<RangeHighlighter> highlighters = null;
115     if (editor != null) {
116       highlighters = RefactoringUtil.highlightAllOccurences(project, occurrences, editor);
117     }
118
119     PsiElement tempAnchorElement = RefactoringUtil.getParentExpressionAnchorElement(selectedExpr);
120
121     final Settings settings =
122       showRefactoringDialog(project, editor, myParentClass, selectedExpr, tempType,
123                             occurrences, tempAnchorElement, anchorStatementIfAll);
124
125     if (settings == null) return false;
126
127     if (settings.getForcedType() != null) {
128       tempType = settings.getForcedType();
129     }
130     final PsiType type = tempType;
131
132     final String fieldName = settings.getFieldName();
133     final PsiElement anchorElementIfOne = tempAnchorElement;
134     final boolean replaceAll = settings.isReplaceAll();
135     if (replaceAll) {
136       tempAnchorElement = anchorStatementIfAll;
137     }
138     final PsiElement anchorElement = tempAnchorElement;
139
140
141     if (editor != null) {
142       HighlightManager highlightManager = HighlightManager.getInstance(project);
143       for (RangeHighlighter highlighter : highlighters) {
144         highlightManager.removeSegmentHighlighter(editor, highlighter);
145       }
146     }
147
148     PsiElement anchor = getNormalizedAnchor(anchorElement);
149
150     boolean tempDeleteSelf = false;
151     if (element.getParent() instanceof PsiExpressionStatement && anchor.equals(anchorElement)) {
152       PsiStatement statement = (PsiStatement)element.getParent();
153       if (statement.getParent() instanceof PsiCodeBlock) {
154         tempDeleteSelf = true;
155       }
156     }
157     final boolean deleteSelf = tempDeleteSelf;
158
159     final Runnable runnable = new Runnable() {
160       public void run() {
161         try {
162           PsiExpression expr = selectedExpr;
163           InitializationPlace initializerPlace = settings.getInitializerPlace();
164           final PsiLocalVariable localVariable = settings.getLocalVariable();
165           final boolean deleteLocalVariable = settings.isDeleteLocalVariable();
166           PsiExpression initializer;
167           if (localVariable != null) {
168             initializer = localVariable.getInitializer();
169           }
170           else {
171             initializer = expr;
172           }
173
174           final PsiMethod enclosingConstructor = getEnclosingConstructor(myParentClass, anchorElement);
175           final PsiClass destClass = settings.getDestinationClass() == null ? myParentClass : settings.getDestinationClass();
176
177           if (!CommonRefactoringUtil.checkReadOnlyStatus(project, destClass.getContainingFile())) return;
178
179           ChangeContextUtil.encodeContextInfo(initializer, true);
180           PsiField field = settings.isIntroduceEnumConstant() ? EnumConstantsUtil.createEnumConstant(destClass, fieldName, initializer) : createField(fieldName, type, initializer, initializerPlace == InitializationPlace.IN_FIELD_DECLARATION && initializer != null);
181           if (!settings.isIntroduceEnumConstant()) {
182             PsiUtil.setModifierProperty(field, settings.getFieldVisibility(), true);
183             if (settings.isDeclareFinal()) {
184               PsiUtil.setModifierProperty(field, PsiModifier.FINAL, true);
185             }
186             if (settings.isDeclareStatic()) {
187               PsiUtil.setModifierProperty(field, PsiModifier.STATIC, true);
188             }
189             if (settings.isAnnotateAsNonNls()) {
190               PsiAnnotation annotation = JavaPsiFacade.getInstance(myParentClass.getProject()).getElementFactory()
191                 .createAnnotationFromText("@" + AnnotationUtil.NON_NLS, myParentClass);
192               field.getModifierList().addAfter(annotation, null);
193               JavaCodeStyleManager.getInstance(myParentClass.getProject()).shortenClassReferences(field.getModifierList());
194             }
195           }
196           PsiElement finalAnchorElement = null;
197           if (destClass == myParentClass) {
198             for (finalAnchorElement = anchorElement;
199                  finalAnchorElement != null && finalAnchorElement.getParent() != destClass;
200                  finalAnchorElement = finalAnchorElement.getParent()) {
201
202             }
203           }
204           PsiMember anchorMember = finalAnchorElement instanceof PsiMember ? (PsiMember)finalAnchorElement : null;
205
206           if ((anchorMember instanceof PsiField) &&
207               anchorMember.hasModifierProperty(PsiModifier.STATIC) == field.hasModifierProperty(PsiModifier.STATIC)) {
208             field = (PsiField)destClass.addBefore(field, anchorMember);
209           }
210           else if (anchorMember instanceof PsiClassInitializer) {
211             field = (PsiField)destClass.addBefore(field, anchorMember);
212             destClass.addBefore(CodeEditUtil.createLineFeed(field.getManager()), anchorMember);
213           }
214           else {
215             field = (PsiField)destClass.add(field);
216           }
217           PsiStatement assignStatement = null;
218           PsiElement anchorElementHere = null;
219           if (initializerPlace == InitializationPlace.IN_CURRENT_METHOD && initializer != null ||
220               initializerPlace == InitializationPlace.IN_CONSTRUCTOR && enclosingConstructor != null && initializer != null) {
221             if (replaceAll) {
222               if (enclosingConstructor != null) {
223                 final PsiElement anchorInConstructor = occurenceManager.getAnchorStatementForAllInScope(enclosingConstructor);
224                 anchorElementHere = anchorInConstructor != null ? anchorInConstructor : anchorStatementIfAll;
225               }
226               else {
227                 anchorElementHere = anchorStatementIfAll;
228               }
229             }
230             else {
231               anchorElementHere = anchorElementIfOne;
232             }
233             assignStatement = createAssignment(field, initializer, anchorElementHere);
234             if (!IntroduceVariableBase.isLoopOrIf(anchorElementHere.getParent())) {
235               anchorElementHere.getParent().addBefore(assignStatement, getNormalizedAnchor(anchorElementHere));
236             }
237           }
238           if (initializerPlace == InitializationPlace.IN_CONSTRUCTOR && initializer != null) {
239             addInitializationToConstructors(initializer, field, enclosingConstructor);
240           }
241           if (initializerPlace == InitializationPlace.IN_SETUP_METHOD && initializer != null) {
242             addInitializationToSetUp(initializer, field, occurenceManager, replaceAll);
243           }
244           if (expr.getParent() instanceof PsiParenthesizedExpression) {
245             expr = (PsiExpression)expr.getParent();
246           }
247           if (deleteSelf) {
248             element.getParent().delete();
249           }
250
251           if (replaceAll) {
252             List<PsiElement> array = new ArrayList<PsiElement>();
253             for (PsiExpression occurrence : occurrences) {
254               if (occurrence instanceof PsiExpression) {
255                 occurrence = RefactoringUtil.outermostParenthesizedExpression(occurrence);
256               }
257               if (deleteSelf && occurrence.equals(expr)) continue;
258               final PsiElement replaced = RefactoringUtil.replaceOccurenceWithFieldRef(occurrence, field, destClass);
259               if (replaced != null) {
260                 array.add(replaced);
261               }
262             }
263
264             if (editor != null) {
265               if (!ApplicationManager.getApplication().isUnitTestMode()) {
266                 PsiElement[] exprsToHighlight = array.toArray(new PsiElement[array.size()]);
267                 HighlightManager highlightManager = HighlightManager.getInstance(project);
268                 highlightManager.addOccurrenceHighlights(editor, exprsToHighlight, highlightAttributes(), true, null);
269                 WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
270               }
271             }
272           }
273           else {
274             if (!deleteSelf) {
275               expr = RefactoringUtil.outermostParenthesizedExpression(expr);
276               RefactoringUtil.replaceOccurenceWithFieldRef(expr, field, destClass);
277             }
278           }
279
280           if (anchorElementHere != null && IntroduceVariableBase.isLoopOrIf(anchorElementHere.getParent())) {
281             IntroduceVariableBase.putStatementInLoopBody(assignStatement, anchorElementHere.getParent(), anchorElementHere);
282           }
283
284
285           if (localVariable != null) {
286             if (deleteLocalVariable) {
287               localVariable.normalizeDeclaration();
288               localVariable.getParent().delete();
289             }
290           }
291
292           ChangeContextUtil.clearContextInfo(initializer);
293         }
294         catch (IncorrectOperationException e) {
295           LOG.error(e);
296         }
297       }
298     };
299
300     CommandProcessor.getInstance().executeCommand(
301       project,
302       new Runnable() {
303         public void run() {
304           ApplicationManager.getApplication().runWriteAction(runnable);
305         }
306       },
307       getRefactoringName(), null
308       );
309
310     return true;
311   }
312
313   private static PsiElement getPhysicalElement(final PsiExpression selectedExpr) {
314     PsiElement element = selectedExpr.getUserData(ElementToWorkOn.PARENT);
315     if (element == null) element = selectedExpr;
316     return element;
317   }
318
319   private static TextAttributes highlightAttributes() {
320     return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(
321                 EditorColors.SEARCH_RESULT_ATTRIBUTES
322               );
323   }
324
325   protected abstract OccurenceManager createOccurenceManager(PsiExpression selectedExpr, PsiClass parentClass);
326
327   protected final PsiClass getParentClass() {
328     return myParentClass;
329   }
330
331   protected abstract boolean validClass(PsiClass parentClass, Editor editor);
332
333   protected boolean isStaticField() {
334     return false;
335   }
336
337   private static PsiElement getNormalizedAnchor(PsiElement anchorElement) {
338     PsiElement child = anchorElement;
339     while (child != null) {
340       PsiElement prev = child.getPrevSibling();
341       if (RefactoringUtil.isExpressionAnchorElement(prev)) break;
342       if (prev instanceof PsiJavaToken && ((PsiJavaToken)prev).getTokenType() == JavaTokenType.LBRACE) break;
343       child = prev;
344     }
345
346     child = PsiTreeUtil.skipSiblingsForward(child, PsiWhiteSpace.class, PsiComment.class);
347     PsiElement anchor;
348     if (child != null) {
349       anchor = child;
350     }
351     else {
352       anchor = anchorElement;
353     }
354     return anchor;
355   }
356
357   protected abstract String getHelpID();
358
359   protected abstract Settings showRefactoringDialog(Project project, Editor editor, PsiClass parentClass, PsiExpression expr,
360                                                     PsiType type, PsiExpression[] occurences, PsiElement anchorElement,
361                                                     PsiElement anchorElementIfAll);
362
363
364   private static PsiType getTypeByExpression(PsiExpression expr) {
365     return RefactoringUtil.getTypeByExpressionWithExpectedType(expr);
366   }
367
368   public PsiClass getParentClass(PsiExpression initializerExpression) {
369     PsiElement element = initializerExpression.getUserData(ElementToWorkOn.PARENT);
370     if (element == null) element = initializerExpression.getParent();
371     PsiElement parent = element;
372     while (parent != null) {
373       if (parent instanceof PsiClass && !(parent instanceof PsiAnonymousClass)) {
374         return (PsiClass)parent;
375       }
376       parent = parent.getParent();
377     }
378     return null;
379   }
380
381   public static PsiMethod getEnclosingConstructor(PsiClass parentClass, PsiElement element) {
382     if (element == null) return null;
383     final PsiMethod[] constructors = parentClass.getConstructors();
384     for (PsiMethod constructor : constructors) {
385       if (PsiTreeUtil.isAncestor(constructor, element, false)) return constructor;
386     }
387     return null;
388   }
389
390   private void addInitializationToSetUp(final PsiExpression initializer,
391                                         final PsiField field,
392                                         final OccurenceManager occurenceManager, final boolean replaceAll) throws IncorrectOperationException {
393     final PsiMethod setupMethod = TestUtil.findSetUpMethod(myParentClass);
394
395     assert setupMethod != null;
396
397     PsiElement anchor = null;
398     if (PsiTreeUtil.isAncestor(setupMethod, initializer, true)) {
399       anchor = replaceAll
400                ? occurenceManager.getAnchorStatementForAllInScope(setupMethod)
401                : PsiTreeUtil.getParentOfType(initializer, PsiStatement.class);
402     }
403
404     final PsiExpressionStatement expressionStatement =
405       (PsiExpressionStatement)JavaPsiFacade.getInstance(myParentClass.getProject()).getElementFactory()
406         .createStatementFromText(field.getName() + "= expr;", null);
407     PsiAssignmentExpression expr = (PsiAssignmentExpression)expressionStatement.getExpression();
408     final PsiExpression rExpression = expr.getRExpression();
409     LOG.assertTrue(rExpression != null);
410     rExpression.replace(initializer);
411
412     final PsiCodeBlock body = setupMethod.getBody();
413     assert body != null;
414     body.addBefore(expressionStatement, anchor);
415   }
416
417   private void addInitializationToConstructors(PsiExpression initializerExpression, PsiField field, PsiMethod enclosingConstructor) {
418     try {
419       PsiClass aClass = field.getContainingClass();
420       PsiMethod[] constructors = aClass.getConstructors();
421
422       boolean added = false;
423       for (PsiMethod constructor : constructors) {
424         if (constructor == enclosingConstructor) continue;
425         PsiCodeBlock body = constructor.getBody();
426         if (body == null) continue;
427         PsiStatement[] statements = body.getStatements();
428         if (statements.length > 0) {
429           PsiStatement first = statements[0];
430           if (first instanceof PsiExpressionStatement) {
431             PsiExpression expression = ((PsiExpressionStatement)first).getExpression();
432             if (expression instanceof PsiMethodCallExpression) {
433               @NonNls String text = ((PsiMethodCallExpression)expression).getMethodExpression().getText();
434               if ("this".equals(text)) {
435                 continue;
436               }
437             }
438           }
439         }
440         PsiStatement assignment = createAssignment(field, initializerExpression, body.getLastChild());
441         assignment = (PsiStatement) body.add(assignment);
442         ChangeContextUtil.decodeContextInfo(assignment, field.getContainingClass(),
443                                             RefactoringUtil.createThisExpression(field.getManager(), null));
444         added = true;
445       }
446       if (!added && enclosingConstructor == null) {
447         PsiElementFactory factory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory();
448         PsiMethod constructor = (PsiMethod)aClass.add(factory.createConstructor());
449         final PsiCodeBlock body = constructor.getBody();
450         PsiStatement assignment = createAssignment(field, initializerExpression, body.getLastChild());
451         assignment = (PsiStatement) body.add(assignment);
452         ChangeContextUtil.decodeContextInfo(assignment, field.getContainingClass(),
453                                             RefactoringUtil.createThisExpression(field.getManager(), null));
454       }
455     }
456     catch (IncorrectOperationException e) {
457       LOG.error(e);
458     }
459   }
460
461   private PsiField createField(String fieldName, PsiType type, PsiExpression initializerExpr, boolean includeInitializer) {
462     @NonNls StringBuilder pattern = new StringBuilder();
463     pattern.append("private int ");
464     pattern.append(fieldName);
465     if (includeInitializer) {
466       pattern.append("=0");
467     }
468     pattern.append(";");
469     PsiManager psiManager = myParentClass.getManager();
470     PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
471     try {
472       PsiField field = factory.createFieldFromText(pattern.toString(), null);
473       field = (PsiField)CodeStyleManager.getInstance(psiManager.getProject()).reformat(field);
474       field.getTypeElement().replace(factory.createTypeElement(type));
475       if (includeInitializer) {
476         field.getInitializer().replace(initializerExpr);
477       }
478       return field;
479     }
480     catch (IncorrectOperationException e) {
481       LOG.error(e);
482       return null;
483     }
484   }
485
486   private PsiStatement createAssignment(PsiField field, PsiExpression initializerExpr, PsiElement context) {
487     try {
488       @NonNls String pattern = "x=0;";
489       PsiManager psiManager = myParentClass.getManager();
490       PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
491       PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(pattern, null);
492       statement = (PsiExpressionStatement)CodeStyleManager.getInstance(psiManager.getProject()).reformat(statement);
493
494       PsiAssignmentExpression expr = (PsiAssignmentExpression)statement.getExpression();
495       final PsiExpression rExpression = expr.getRExpression();
496       LOG.assertTrue(rExpression != null);
497       rExpression.replace(initializerExpr);
498       final PsiReferenceExpression fieldReference = RenameJavaVariableProcessor.createMemberReference(field, context);
499       expr.getLExpression().replace(fieldReference);
500
501       return statement;
502     }
503     catch (IncorrectOperationException e) {
504       LOG.error(e);
505       return null;
506     }
507   }
508   
509
510   protected Pass<ElementToWorkOn> getElementProcessor(final Project project, final Editor editor) {
511     return new Pass<ElementToWorkOn>() {
512       @Override
513       public void pass(final ElementToWorkOn elementToWorkOn) {
514         if (elementToWorkOn == null) return;
515
516         if (elementToWorkOn.getExpression() == null) {
517           final PsiLocalVariable localVariable = elementToWorkOn.getLocalVariable();
518           final boolean result = invokeImpl(project, localVariable, editor);
519           if (result) {
520             editor.getSelectionModel().removeSelection();
521           }
522         }
523         else if (invokeImpl(project, elementToWorkOn.getExpression(), editor)) {
524           editor.getSelectionModel().removeSelection();
525         }
526       }
527     };
528   }
529
530   protected abstract String getRefactoringName();
531
532   public static class Settings {
533     private final String myFieldName;
534     private final PsiType myForcedType;
535
536     private final boolean myReplaceAll;
537     private final boolean myDeclareStatic;
538     private final boolean myDeclareFinal;
539     private final InitializationPlace myInitializerPlace;
540     @Modifier private final String myVisibility;
541     private final boolean myDeleteLocalVariable;
542     private final TargetDestination myTargetClass;
543     private final boolean myAnnotateAsNonNls;
544     private final boolean myIntroduceEnumConstant;
545
546     public PsiLocalVariable getLocalVariable() {
547       return myLocalVariable;
548     }
549
550     public boolean isDeleteLocalVariable() {
551       return myDeleteLocalVariable;
552     }
553
554     private final PsiLocalVariable myLocalVariable;
555
556     public String getFieldName() {
557       return myFieldName;
558     }
559
560     public boolean isDeclareStatic() {
561       return myDeclareStatic;
562     }
563
564     public boolean isDeclareFinal() {
565       return myDeclareFinal;
566     }
567
568     public InitializationPlace getInitializerPlace() {
569       return myInitializerPlace;
570     }
571
572     @Modifier
573     public String getFieldVisibility() {
574       return myVisibility;
575     }
576
577     @Nullable
578     public PsiClass getDestinationClass() {
579       return myTargetClass != null ? myTargetClass.getTargetClass() : null;
580     }
581
582     public PsiType getForcedType() {
583       return myForcedType;
584     }
585
586     public boolean isReplaceAll() {
587       return myReplaceAll;
588     }
589
590     public boolean isAnnotateAsNonNls() {
591       return myAnnotateAsNonNls;
592     }
593
594     public boolean isIntroduceEnumConstant() {
595       return myIntroduceEnumConstant;
596     }
597
598     public Settings(String fieldName, boolean replaceAll,
599                     boolean declareStatic, boolean declareFinal,
600                     InitializationPlace initializerPlace, @Modifier String visibility, PsiLocalVariable localVariableToRemove, PsiType forcedType,
601                     boolean deleteLocalVariable,
602                     TargetDestination targetDestination,
603                     final boolean annotateAsNonNls,
604                     final boolean introduceEnumConstant) {
605
606       myFieldName = fieldName;
607       myReplaceAll = replaceAll;
608       myDeclareStatic = declareStatic;
609       myDeclareFinal = declareFinal;
610       myInitializerPlace = initializerPlace;
611       myVisibility = visibility;
612       myLocalVariable = localVariableToRemove;
613       myDeleteLocalVariable = deleteLocalVariable;
614       myForcedType = forcedType;
615       myTargetClass = targetDestination;
616       myAnnotateAsNonNls = annotateAsNonNls;
617       myIntroduceEnumConstant = introduceEnumConstant;
618     }
619
620     public Settings(String fieldName, boolean replaceAll,
621                     boolean declareStatic, boolean declareFinal,
622                     InitializationPlace initializerPlace, @Modifier String visibility, PsiLocalVariable localVariableToRemove, PsiType forcedType,
623                     boolean deleteLocalVariable,
624                     PsiClass targetClass,
625                     final boolean annotateAsNonNls,
626                     final boolean introduceEnumConstant) {
627
628       this(fieldName, replaceAll, declareStatic, declareFinal, initializerPlace, visibility, localVariableToRemove, forcedType, deleteLocalVariable, new TargetDestination(targetClass), annotateAsNonNls, introduceEnumConstant);
629     }
630
631   }
632
633   public static class TargetDestination {
634     private final String myQualifiedName;
635     private final Project myProject;
636
637     private PsiClass myParentClass;
638     private PsiClass myTargetClass;
639
640     public TargetDestination(String qualifiedName, PsiClass parentClass) {
641       myQualifiedName = qualifiedName;
642       myParentClass = parentClass;
643       myProject = parentClass.getProject();
644     }
645
646     public TargetDestination(@NotNull PsiClass targetClass) {
647       myTargetClass = targetClass;
648       myQualifiedName = targetClass.getQualifiedName();
649       myProject = targetClass.getProject();
650     }
651
652     @Nullable
653     public PsiClass getTargetClass() {
654       if (myTargetClass != null) return myTargetClass;
655       final String packageName = StringUtil.getPackageName(myQualifiedName);
656       PsiPackage psiPackage = JavaPsiFacade.getInstance(myProject).findPackage(packageName);
657       final PsiDirectory psiDirectory;
658       if (psiPackage != null) {
659         final PsiDirectory[] directories = psiPackage.getDirectories(GlobalSearchScope.allScope(myProject));
660         psiDirectory = directories.length > 1 ? DirectoryChooserUtil.chooseDirectory(directories, null, myProject, new HashMap<PsiDirectory, String>()) : directories[0];
661       } else {
662         psiDirectory = PackageUtil.findOrCreateDirectoryForPackage(myProject, packageName, myParentClass.getContainingFile().getContainingDirectory(), false);
663       }
664       final String shortName = StringUtil.getShortName(myQualifiedName);
665       myTargetClass = psiDirectory != null ? JavaDirectoryService.getInstance().createClass(psiDirectory, shortName) : null;
666       return myTargetClass;
667     }
668   }
669 }