Merge remote-tracking branch 'origin/master'
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / introduceVariable / IntroduceVariableBase.java
1 /*
2  * Copyright 2000-2015 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.introduceVariable;
17
18 import com.intellij.codeInsight.CodeInsightUtil;
19 import com.intellij.codeInsight.NullableNotNullManager;
20 import com.intellij.codeInsight.completion.JavaCompletionUtil;
21 import com.intellij.codeInsight.highlighting.HighlightManager;
22 import com.intellij.codeInsight.lookup.LookupManager;
23 import com.intellij.codeInsight.unwrap.ScopeHighlighter;
24 import com.intellij.featureStatistics.FeatureUsageTracker;
25 import com.intellij.featureStatistics.ProductivityFeatureNames;
26 import com.intellij.ide.util.PropertiesComponent;
27 import com.intellij.lang.LanguageRefactoringSupport;
28 import com.intellij.lang.injection.InjectedLanguageManager;
29 import com.intellij.lang.refactoring.RefactoringSupportProvider;
30 import com.intellij.openapi.actionSystem.DataContext;
31 import com.intellij.openapi.application.ApplicationManager;
32 import com.intellij.openapi.command.CommandProcessor;
33 import com.intellij.openapi.diagnostic.Logger;
34 import com.intellij.openapi.editor.*;
35 import com.intellij.openapi.editor.colors.EditorColors;
36 import com.intellij.openapi.editor.colors.EditorColorsManager;
37 import com.intellij.openapi.editor.markup.TextAttributes;
38 import com.intellij.openapi.fileEditor.FileDocumentManager;
39 import com.intellij.openapi.project.Project;
40 import com.intellij.openapi.util.Computable;
41 import com.intellij.openapi.util.Key;
42 import com.intellij.openapi.util.Pass;
43 import com.intellij.openapi.util.TextRange;
44 import com.intellij.openapi.util.registry.Registry;
45 import com.intellij.openapi.util.text.StringUtil;
46 import com.intellij.openapi.wm.WindowManager;
47 import com.intellij.psi.*;
48 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
49 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
50 import com.intellij.psi.codeStyle.SuggestedNameInfo;
51 import com.intellij.psi.codeStyle.VariableKind;
52 import com.intellij.psi.impl.PsiDiamondTypeUtil;
53 import com.intellij.psi.impl.source.jsp.jspJava.JspCodeBlock;
54 import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
55 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
56 import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil;
57 import com.intellij.psi.scope.processor.VariablesProcessor;
58 import com.intellij.psi.scope.util.PsiScopesUtil;
59 import com.intellij.psi.util.*;
60 import com.intellij.refactoring.*;
61 import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
62 import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
63 import com.intellij.refactoring.introduceField.ElementToWorkOn;
64 import com.intellij.refactoring.listeners.RefactoringEventData;
65 import com.intellij.refactoring.listeners.RefactoringEventListener;
66 import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
67 import com.intellij.refactoring.util.CommonRefactoringUtil;
68 import com.intellij.refactoring.util.FieldConflictsResolver;
69 import com.intellij.refactoring.util.RefactoringUIUtil;
70 import com.intellij.refactoring.util.RefactoringUtil;
71 import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager;
72 import com.intellij.refactoring.util.occurrences.NotInSuperCallOccurrenceFilter;
73 import com.intellij.util.ArrayUtilRt;
74 import com.intellij.util.IncorrectOperationException;
75 import com.intellij.util.containers.ContainerUtil;
76 import com.intellij.util.containers.MultiMap;
77 import org.jetbrains.annotations.NonNls;
78 import org.jetbrains.annotations.NotNull;
79 import org.jetbrains.annotations.Nullable;
80
81 import java.util.*;
82
83 /**
84  * @author dsl
85  * Date: Nov 15, 2002
86  */
87 public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
88   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceVariable.IntroduceVariableBase");
89   @NonNls private static final String PREFER_STATEMENTS_OPTION = "introduce.variable.prefer.statements";
90   @NonNls private static final String REFACTORING_ID = "refactoring.extractVariable";
91   
92   protected static final String REFACTORING_NAME = RefactoringBundle.message("introduce.variable.title");
93   public static final Key<Boolean> NEED_PARENTHESIS = Key.create("NEED_PARENTHESIS");
94   private JavaVariableInplaceIntroducer myInplaceIntroducer;
95
96   public static SuggestedNameInfo getSuggestedName(@Nullable PsiType type, @NotNull final PsiExpression expression) {
97     return getSuggestedName(type, expression, expression);
98   }
99
100   public static SuggestedNameInfo getSuggestedName(@Nullable PsiType type,
101                                                    @NotNull final PsiExpression expression,
102                                                    final PsiElement anchor) {
103     final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(expression.getProject());
104     final SuggestedNameInfo nameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expression, type);
105     final String[] strings = JavaCompletionUtil
106       .completeVariableNameForRefactoring(codeStyleManager, type, VariableKind.LOCAL_VARIABLE, nameInfo);
107     final SuggestedNameInfo.Delegate delegate = new SuggestedNameInfo.Delegate(strings, nameInfo);
108     return codeStyleManager.suggestUniqueVariableName(delegate, anchor, true);
109   }
110
111   public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) {
112     final SelectionModel selectionModel = editor.getSelectionModel();
113     if (!selectionModel.hasSelection()) {
114       final int offset = editor.getCaretModel().getOffset();
115       final PsiElement[] statementsInRange = findStatementsAtOffset(editor, file, offset);
116
117       //try line selection
118       if (statementsInRange.length == 1 && selectLineAtCaret(offset, statementsInRange)) {
119         selectionModel.selectLineAtCaret();
120         final PsiExpression expressionInRange =
121           findExpressionInRange(project, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
122         if (expressionInRange == null || getErrorMessage(expressionInRange) != null) {
123           selectionModel.removeSelection();
124         }
125       }
126
127       if (!selectionModel.hasSelection()) {
128         final List<PsiExpression> expressions = collectExpressions(file, editor, offset);
129         if (expressions.isEmpty()) {
130           selectionModel.selectLineAtCaret();
131         } else if (!isChooserNeeded(expressions)) {
132           final TextRange textRange = expressions.get(0).getTextRange();
133           selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset());
134         }
135         else {
136           IntroduceTargetChooser.showChooser(editor, expressions,
137             new Pass<PsiExpression>(){
138               public void pass(final PsiExpression selectedValue) {
139                 invoke(project, editor, file, selectedValue.getTextRange().getStartOffset(), selectedValue.getTextRange().getEndOffset());
140               }
141             },
142             new PsiExpressionTrimRenderer.RenderFunction(), "Expressions", preferredSelection(statementsInRange, expressions), ScopeHighlighter.NATURAL_RANGER);
143           return;
144         }
145       }
146     }
147     if (invoke(project, editor, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()) &&
148         LookupManager.getActiveLookup(editor) == null) {
149       selectionModel.removeSelection();
150     }
151   }
152
153   public static boolean isChooserNeeded(List<PsiExpression> exprs) {
154     if (exprs.size() == 1) {
155       final PsiExpression expression = exprs.get(0);
156       return expression instanceof PsiNewExpression && ((PsiNewExpression)expression).getAnonymousClass() != null;
157     }
158     return true;
159   }
160   
161   public static boolean selectLineAtCaret(int offset, PsiElement[] statementsInRange) {
162     TextRange range = statementsInRange[0].getTextRange();
163     if (statementsInRange[0] instanceof PsiExpressionStatement) {
164       range = ((PsiExpressionStatement)statementsInRange[0]).getExpression().getTextRange();
165     }
166
167     return range.getStartOffset() > offset ||
168            range.getEndOffset() <= offset ||
169            isPreferStatements();
170   }
171
172   public static int preferredSelection(PsiElement[] statementsInRange, List<PsiExpression> expressions) {
173     int selection;
174     if (statementsInRange.length == 1 &&
175         statementsInRange[0] instanceof PsiExpressionStatement &&
176         PsiUtilCore.hasErrorElementChild(statementsInRange[0])) {
177       selection = expressions.indexOf(((PsiExpressionStatement)statementsInRange[0]).getExpression());
178     } else {
179       PsiExpression expression = expressions.get(0);
180       if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() instanceof PsiLocalVariable) {
181         selection = 1;
182       }
183       else {
184         selection = -1;
185       }
186     }
187     return selection;
188   }
189
190   public static boolean isPreferStatements() {
191     return Boolean.valueOf(PropertiesComponent.getInstance().getBoolean(PREFER_STATEMENTS_OPTION)) || Registry.is(PREFER_STATEMENTS_OPTION, false);
192   }
193
194   public static List<PsiExpression> collectExpressions(final PsiFile file,
195                                                        final Editor editor,
196                                                        final int offset) {
197     return collectExpressions(file, editor, offset, false);
198   }
199
200   public static List<PsiExpression> collectExpressions(final PsiFile file,
201                                                        final Editor editor,
202                                                        final int offset,
203                                                        boolean acceptVoid) {
204     return collectExpressions(file, editor.getDocument(), offset, acceptVoid);
205   }
206
207   public static List<PsiExpression> collectExpressions(final PsiFile file,
208                                                        final Document document,
209                                                        final int offset,
210                                                        boolean acceptVoid) {
211     CharSequence text = document.getCharsSequence();
212     int correctedOffset = offset;
213     int textLength = document.getTextLength();
214     if (offset >= textLength) {
215       correctedOffset = textLength - 1;
216     }
217     else if (!Character.isJavaIdentifierPart(text.charAt(offset))) {
218       correctedOffset--;
219     }
220     if (correctedOffset < 0) {
221       correctedOffset = offset;
222     }
223     else if (!Character.isJavaIdentifierPart(text.charAt(correctedOffset))) {
224       if (text.charAt(correctedOffset) == ';') {//initially caret on the end of line
225         correctedOffset--;
226       }
227       if (correctedOffset < 0 || text.charAt(correctedOffset) != ')') {
228         correctedOffset = offset;
229       }
230     }
231     final PsiElement elementAtCaret = file.findElementAt(correctedOffset);
232     final List<PsiExpression> expressions = new ArrayList<>();
233     /*for (PsiElement element : statementsInRange) {
234       if (element instanceof PsiExpressionStatement) {
235         final PsiExpression expression = ((PsiExpressionStatement)element).getExpression();
236         if (expression.getType() != PsiType.VOID) {
237           expressions.add(expression);
238         }
239       }
240     }*/
241     PsiExpression expression = PsiTreeUtil.getParentOfType(elementAtCaret, PsiExpression.class);
242     while (expression != null) {
243       if (!expressions.contains(expression) && !(expression instanceof PsiParenthesizedExpression) && !(expression instanceof PsiSuperExpression) &&
244           (acceptVoid || !PsiType.VOID.equals(expression.getType()))) {
245         if (expression instanceof PsiMethodReferenceExpression) {
246           expressions.add(expression);
247         }
248         else if (!(expression instanceof PsiAssignmentExpression)) {
249           if (!(expression instanceof PsiReferenceExpression)) {
250             expressions.add(expression);
251           }
252           else {
253             if (!(expression.getParent() instanceof PsiMethodCallExpression)) {
254               final PsiElement resolve = ((PsiReferenceExpression)expression).resolve();
255               if (!(resolve instanceof PsiClass) && !(resolve instanceof PsiPackage)) {
256                 expressions.add(expression);
257               }
258             }
259           }
260         }
261       }
262       expression = PsiTreeUtil.getParentOfType(expression, PsiExpression.class);
263     }
264     return expressions;
265   }
266
267   public static PsiElement[] findStatementsAtOffset(final Editor editor, final PsiFile file, final int offset) {
268     final Document document = editor.getDocument();
269     final int lineNumber = document.getLineNumber(offset);
270     final int lineStart = document.getLineStartOffset(lineNumber);
271     final int lineEnd = document.getLineEndOffset(lineNumber);
272
273     return CodeInsightUtil.findStatementsInRange(file, lineStart, lineEnd);
274   }
275
276   private boolean invoke(final Project project, final Editor editor, PsiFile file, int startOffset, int endOffset) {
277     FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.REFACTORING_INTRODUCE_VARIABLE);
278     PsiDocumentManager.getInstance(project).commitAllDocuments();
279
280
281     return invokeImpl(project, findExpressionInRange(project, file, startOffset, endOffset), editor);
282   }
283
284   private static PsiExpression findExpressionInRange(Project project, PsiFile file, int startOffset, int endOffset) {
285     PsiExpression tempExpr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset);
286     if (tempExpr == null) {
287       PsiElement[] statements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
288       if (statements.length == 1) {
289         if (statements[0] instanceof PsiExpressionStatement) {
290           tempExpr = ((PsiExpressionStatement) statements[0]).getExpression();
291         } else if (statements[0] instanceof PsiReturnStatement) {
292           tempExpr = ((PsiReturnStatement)statements[0]).getReturnValue();
293         }
294       }
295     }
296
297     if (tempExpr == null) {
298       tempExpr = getSelectedExpression(project, file, startOffset, endOffset);
299     }
300     return tempExpr;
301   }
302
303   /**
304    * @return can return NotNull value although extraction will fail: reason could be retrieved from {@link #getErrorMessage(PsiExpression)}
305    */
306   public static PsiExpression getSelectedExpression(final Project project, PsiFile file, int startOffset, int endOffset) {
307     final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(project);
308     PsiElement elementAtStart = file.findElementAt(startOffset);
309     if (elementAtStart == null || elementAtStart instanceof PsiWhiteSpace || elementAtStart instanceof PsiComment) {
310       final PsiElement element = PsiTreeUtil.skipSiblingsForward(elementAtStart, PsiWhiteSpace.class, PsiComment.class);
311       if (element != null) {
312         startOffset = element.getTextOffset();
313         elementAtStart = file.findElementAt(startOffset);
314       }
315       if (elementAtStart == null) {
316         if (injectedLanguageManager.isInjectedFragment(file)) {
317           return getSelectionFromInjectedHost(project, file, injectedLanguageManager, startOffset, endOffset);
318         } else {
319           return null;
320         }
321       }
322       startOffset = elementAtStart.getTextOffset();
323     }
324     PsiElement elementAtEnd = file.findElementAt(endOffset - 1);
325     if (elementAtEnd == null || elementAtEnd instanceof PsiWhiteSpace || elementAtEnd instanceof PsiComment) {
326       elementAtEnd = PsiTreeUtil.skipSiblingsBackward(elementAtEnd, PsiWhiteSpace.class, PsiComment.class);
327       if (elementAtEnd == null) return null;
328       endOffset = elementAtEnd.getTextRange().getEndOffset();
329     }
330
331     if (endOffset <= startOffset) return null;
332
333     PsiElement elementAt = PsiTreeUtil.findCommonParent(elementAtStart, elementAtEnd);
334     final PsiExpression containingExpression = PsiTreeUtil.getParentOfType(elementAt, PsiExpression.class, false);
335
336     if (containingExpression != null && containingExpression == elementAtEnd && startOffset == containingExpression.getTextOffset()) {
337       return containingExpression;
338     }
339     
340     if (containingExpression == null || containingExpression instanceof PsiLambdaExpression) {
341       if (injectedLanguageManager.isInjectedFragment(file)) {
342         return getSelectionFromInjectedHost(project, file, injectedLanguageManager, startOffset, endOffset);
343       }
344       elementAt = null;
345     }
346     final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(elementAt, PsiLiteralExpression.class);
347
348     final PsiLiteralExpression startLiteralExpression = PsiTreeUtil.getParentOfType(elementAtStart, PsiLiteralExpression.class);
349     final PsiLiteralExpression endLiteralExpression = PsiTreeUtil.getParentOfType(file.findElementAt(endOffset), PsiLiteralExpression.class);
350
351     final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
352     String text = null;
353     PsiExpression tempExpr;
354     try {
355       text = file.getText().subSequence(startOffset, endOffset).toString();
356       String prefix = null;
357       if (startLiteralExpression != null) {
358         final int startExpressionOffset = startLiteralExpression.getTextOffset();
359         if (startOffset == startExpressionOffset + 1) {
360           text = "\"" + text;
361         } else if (startOffset > startExpressionOffset + 1){
362           prefix = "\" + ";
363           text = "\"" + text;
364         }
365       }
366
367       String suffix = null;
368       if (endLiteralExpression != null) {
369         final int endExpressionOffset = endLiteralExpression.getTextOffset() + endLiteralExpression.getTextLength();
370         if (endOffset == endExpressionOffset - 1) {
371           text += "\"";
372         } else if (endOffset < endExpressionOffset - 1) {
373           suffix = " + \"";
374           text += "\"";
375         }
376       }
377
378       if (literalExpression != null && text.equals(literalExpression.getText())) return literalExpression;
379
380       final PsiElement parent = literalExpression != null ? literalExpression : elementAt;
381       tempExpr = elementFactory.createExpressionFromText(text, parent);
382
383       final boolean [] hasErrors = new boolean[1];
384       final JavaRecursiveElementWalkingVisitor errorsVisitor = new JavaRecursiveElementWalkingVisitor() {
385         @Override
386         public void visitElement(final PsiElement element) {
387           if (hasErrors[0]) {
388             return;
389           }
390           super.visitElement(element);
391         }
392
393         @Override
394         public void visitErrorElement(final PsiErrorElement element) {
395           hasErrors[0] = true;
396         }
397       };
398       tempExpr.accept(errorsVisitor);
399       if (hasErrors[0]) return null;
400
401       tempExpr.putUserData(ElementToWorkOn.PREFIX, prefix);
402       tempExpr.putUserData(ElementToWorkOn.SUFFIX, suffix);
403
404       final RangeMarker rangeMarker =
405         FileDocumentManager.getInstance().getDocument(file.getVirtualFile()).createRangeMarker(startOffset, endOffset);
406       tempExpr.putUserData(ElementToWorkOn.TEXT_RANGE, rangeMarker);
407
408       if (parent != null) {
409         tempExpr.putUserData(ElementToWorkOn.PARENT, parent);
410       }
411       else {
412         PsiErrorElement errorElement = elementAtStart instanceof PsiErrorElement
413                                        ? (PsiErrorElement)elementAtStart
414                                        : PsiTreeUtil.getNextSiblingOfType(elementAtStart, PsiErrorElement.class);
415         if (errorElement == null) {
416           errorElement = PsiTreeUtil.getParentOfType(elementAtStart, PsiErrorElement.class);
417         }
418         if (errorElement == null) return null;
419         if (!(errorElement.getParent() instanceof PsiClass)) return null;
420         tempExpr.putUserData(ElementToWorkOn.PARENT, errorElement);
421         tempExpr.putUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK, Boolean.TRUE);
422       }
423
424       final String fakeInitializer = "intellijidearulezzz";
425       final int[] refIdx = new int[1];
426       final PsiElement toBeExpression = createReplacement(fakeInitializer, project, prefix, suffix, parent, rangeMarker, refIdx);
427       toBeExpression.accept(errorsVisitor);
428       if (hasErrors[0]) return null;
429       if (literalExpression != null && toBeExpression instanceof PsiExpression) {
430         PsiType type = ((PsiExpression)toBeExpression).getType();
431         if (type != null && !type.equals(literalExpression.getType())) {
432           return null;
433         }
434       }
435
436       final PsiReferenceExpression refExpr = PsiTreeUtil.getParentOfType(toBeExpression.findElementAt(refIdx[0]), PsiReferenceExpression.class);
437       if (refExpr == null) return null;
438       if (toBeExpression == refExpr && refIdx[0] > 0) {
439         return null;
440       }
441       if (ReplaceExpressionUtil.isNeedParenthesis(refExpr.getNode(), tempExpr.getNode())) {
442         tempExpr.putCopyableUserData(NEED_PARENTHESIS, Boolean.TRUE);
443         return tempExpr;
444       }
445     }
446     catch (IncorrectOperationException e) {
447       if (elementAt instanceof PsiExpressionList) {
448         final PsiElement parent = elementAt.getParent();
449         return parent instanceof PsiCallExpression ? createArrayCreationExpression(text, startOffset, endOffset, (PsiCallExpression)parent) : null;
450       }
451       return null;
452     }
453
454     return tempExpr;
455   }
456
457   private static PsiExpression getSelectionFromInjectedHost(Project project,
458                                                             PsiFile file,
459                                                             InjectedLanguageManager injectedLanguageManager, int startOffset, int endOffset) {
460     final PsiLanguageInjectionHost injectionHost = injectedLanguageManager.getInjectionHost(file);
461     return getSelectedExpression(project, injectionHost.getContainingFile(), injectedLanguageManager.injectedToHost(file, startOffset), injectedLanguageManager.injectedToHost(file, endOffset));
462   }
463
464   @Nullable
465   public static String getErrorMessage(PsiExpression expr) {
466     final Boolean needParenthesis = expr.getCopyableUserData(NEED_PARENTHESIS);
467     if (needParenthesis != null && needParenthesis.booleanValue()) {
468       return "Extracting selected expression would change the semantic of the whole expression.";
469     }
470     return null;
471   }
472
473   private static PsiExpression createArrayCreationExpression(String text, int startOffset, int endOffset, PsiCallExpression parent) {
474     if (text == null || parent == null) return null;
475     final String[] varargsExpressions = text.split("s*,s*");
476     if (varargsExpressions.length > 1) {
477       final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(parent.getProject());
478       final JavaResolveResult resolveResult = parent.resolveMethodGenerics();
479       final PsiMethod psiMethod = (PsiMethod)resolveResult.getElement();
480       if (psiMethod == null || !psiMethod.isVarArgs()) return null;
481       final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
482       final PsiParameter varargParameter = parameters[parameters.length - 1];
483       final PsiType type = varargParameter.getType();
484       LOG.assertTrue(type instanceof PsiEllipsisType);
485       final PsiArrayType psiType = (PsiArrayType)((PsiEllipsisType)type).toArrayType();
486       final PsiExpression[] args = parent.getArgumentList().getExpressions();
487       final PsiSubstitutor psiSubstitutor = resolveResult.getSubstitutor();
488
489       if (args.length < parameters.length || startOffset < args[parameters.length - 1].getTextRange().getStartOffset()) return null;
490
491       final PsiFile containingFile = parent.getContainingFile();
492
493       PsiElement startElement = containingFile.findElementAt(startOffset);
494       while (startElement != null && startElement.getParent() != parent.getArgumentList()) {
495         startElement = startElement.getParent();
496       }
497       if (startElement == null || startOffset > startElement.getTextOffset()) return null;
498
499       PsiElement endElement = containingFile.findElementAt(endOffset - 1);
500       while (endElement != null && endElement.getParent() != parent.getArgumentList()) {
501         endElement = endElement.getParent();
502       }
503       if (endElement == null || endOffset < endElement.getTextRange().getEndOffset()) return null;
504
505       final PsiType componentType = TypeConversionUtil.erasure(psiSubstitutor.substitute(psiType.getComponentType()));
506       try {
507         final PsiExpression expressionFromText =
508           elementFactory.createExpressionFromText("new " + componentType.getCanonicalText() + "[]{" + text + "}", parent);
509         final RangeMarker rangeMarker =
510         FileDocumentManager.getInstance().getDocument(containingFile.getVirtualFile()).createRangeMarker(startOffset, endOffset);
511         expressionFromText.putUserData(ElementToWorkOn.TEXT_RANGE, rangeMarker);
512         expressionFromText.putUserData(ElementToWorkOn.PARENT, parent);
513         return expressionFromText;
514       }
515       catch (IncorrectOperationException e) {
516         return null;
517       }
518     }
519     return null;
520   }
521
522   protected boolean invokeImpl(final Project project, final PsiExpression expr,
523                                final Editor editor) {
524     if (expr != null) {
525       final String errorMessage = getErrorMessage(expr);
526       if (errorMessage != null) {
527         showErrorMessage(project, editor, RefactoringBundle.getCannotRefactorMessage(errorMessage));
528         return false;
529       }
530     }
531
532     if (expr != null && expr.getParent() instanceof PsiExpressionStatement) {
533       FeatureUsageTracker.getInstance().triggerFeatureUsed("refactoring.introduceVariable.incompleteStatement");
534     }
535     if (LOG.isDebugEnabled()) {
536       LOG.debug("expression:" + expr);
537     }
538
539     if (expr == null || !expr.isPhysical()) {
540       if (ReassignVariableUtil.reassign(editor)) return false;
541       if (expr == null) {
542         String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.block.should.represent.an.expression"));
543         showErrorMessage(project, editor, message);
544         return false;
545       }
546     }
547
548
549     final PsiType originalType = RefactoringUtil.getTypeByExpressionWithExpectedType(expr);
550     if (originalType == null || LambdaUtil.notInferredType(originalType)) {
551       String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("unknown.expression.type"));
552       showErrorMessage(project, editor, message);
553       return false;
554     }
555
556     if (PsiType.VOID.equals(originalType)) {
557       String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.expression.has.void.type"));
558       showErrorMessage(project, editor, message);
559       return false;
560     }
561
562
563     final PsiElement physicalElement = expr.getUserData(ElementToWorkOn.PARENT);
564
565     final PsiElement anchorStatement = RefactoringUtil.getParentStatement(physicalElement != null ? physicalElement : expr, false);
566
567     if (anchorStatement == null) {
568       return parentStatementNotFound(project, editor);
569     }
570     if (checkAnchorBeforeThisOrSuper(project, editor, anchorStatement, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE)) return false;
571
572     final PsiElement tempContainer = anchorStatement.getParent();
573
574     if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
575       String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
576       showErrorMessage(project, editor, message);
577       return false;
578     }
579
580     if(!NotInSuperCallOccurrenceFilter.INSTANCE.isOK(expr)) {
581       String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("cannot.introduce.variable.in.super.constructor.call"));
582       showErrorMessage(project, editor, message);
583       return false;
584     }
585
586     final PsiFile file = anchorStatement.getContainingFile();
587     LOG.assertTrue(file != null, "expr.getContainingFile() == null");
588     final PsiElement nameSuggestionContext = editor == null ? null : file.findElementAt(editor.getCaretModel().getOffset());
589     final RefactoringSupportProvider supportProvider = LanguageRefactoringSupport.INSTANCE.forLanguage(expr.getLanguage());
590     final boolean isInplaceAvailableOnDataContext =
591       supportProvider != null &&
592       editor.getSettings().isVariableInplaceRenameEnabled() &&
593       supportProvider.isInplaceIntroduceAvailable(expr, nameSuggestionContext) &&
594       (!ApplicationManager.getApplication().isUnitTestMode() || isInplaceAvailableInTestMode()) &&
595       !isInJspHolderMethod(expr);
596
597     if (isInplaceAvailableOnDataContext) {
598       final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
599       checkInLoopCondition(expr, conflicts);
600       if (!conflicts.isEmpty()) {
601         showErrorMessage(project, editor, StringUtil.join(conflicts.values(), "<br>"));
602         return false;
603       }
604     }
605
606     final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, tempContainer);
607     final PsiExpression[] occurrences = occurrenceManager.getOccurrences();
608     final PsiElement anchorStatementIfAll = occurrenceManager.getAnchorStatementForAll();
609
610
611     final List<PsiExpression> nonWrite = new ArrayList<>();
612     boolean cantReplaceAll = false;
613     boolean cantReplaceAllButWrite = false;
614     for (PsiExpression occurrence : occurrences) {
615       if (!RefactoringUtil.isAssignmentLHS(occurrence)) {
616         nonWrite.add(occurrence);
617       } else if (isFinalVariableOnLHS(occurrence)) {
618         cantReplaceAll = true;
619       } else if (!nonWrite.isEmpty()){
620         cantReplaceAllButWrite = true;
621         cantReplaceAll = true;
622       }
623     }
624     if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return false;
625
626     final LinkedHashMap<OccurrencesChooser.ReplaceChoice, List<PsiExpression>> occurrencesMap = ContainerUtil.newLinkedHashMap();
627     occurrencesMap.put(OccurrencesChooser.ReplaceChoice.NO, Collections.singletonList(expr));
628     final boolean hasWriteAccess = occurrences.length > nonWrite.size() && occurrences.length > 1;
629     if (hasWriteAccess && !cantReplaceAllButWrite) {
630       occurrencesMap.put(OccurrencesChooser.ReplaceChoice.NO_WRITE, nonWrite);
631     }
632
633     if (occurrences.length > 1 && !cantReplaceAll) {
634       occurrencesMap.put(OccurrencesChooser.ReplaceChoice.ALL, Arrays.asList(occurrences));
635     }
636
637     final boolean inFinalContext = occurrenceManager.isInFinalContext();
638     final InputValidator validator = new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurrenceManager);
639     final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences);
640     final boolean[] wasSucceed = new boolean[]{true};
641     final Pass<OccurrencesChooser.ReplaceChoice> callback = new Pass<OccurrencesChooser.ReplaceChoice>() {
642       @Override
643       public void pass(final OccurrencesChooser.ReplaceChoice choice) {
644         if (choice != null) {
645           final boolean replaceAll = choice == OccurrencesChooser.ReplaceChoice.ALL || choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
646           typeSelectorManager.setAllOccurrences(replaceAll);
647
648           final PsiElement chosenAnchor =
649             chooseAnchor(replaceAll, choice == OccurrencesChooser.ReplaceChoice.NO_WRITE, nonWrite, anchorStatementIfAll, anchorStatement);
650           final IntroduceVariableSettings settings =
651             getSettings(project, editor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, chosenAnchor, choice);
652           
653           final boolean cantChangeFinalModifier = (hasWriteAccess || inFinalContext) && choice == OccurrencesChooser.ReplaceChoice.ALL;
654
655           final boolean noWrite = choice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
656           final List<PsiExpression> allOccurrences = new ArrayList<>();
657           for (PsiExpression occurrence : occurrences) {
658             if (expr.equals(occurrence) && expr.getParent() instanceof PsiExpressionStatement) continue;
659             if (choice == OccurrencesChooser.ReplaceChoice.ALL || (noWrite && !PsiUtil.isAccessedForWriting(occurrence)) ||  expr.equals(occurrence)) {
660               allOccurrences.add(occurrence);
661             }
662           }
663           myInplaceIntroducer = new JavaVariableInplaceIntroducer(project,
664                                             settings,
665                                             chosenAnchor,
666                                             editor, expr, cantChangeFinalModifier,
667                                             allOccurrences.toArray(new PsiExpression[allOccurrences.size()]),
668                                             typeSelectorManager,
669                                             REFACTORING_NAME);
670           if (myInplaceIntroducer.startInplaceIntroduceTemplate()) {
671             return;
672           }
673         }
674
675         CommandProcessor.getInstance().executeCommand(
676           project,
677           () -> {
678             final Editor topLevelEditor ;
679             if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) {
680               topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
681             } else {
682               topLevelEditor = editor;
683             }
684
685             PsiVariable variable = null;
686             try {
687               final IntroduceVariableSettings settings =
688                 getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, anchorStatement, choice);
689               if (!settings.isOK()) {
690                 wasSucceed[0] = false;
691                 return;
692               }
693
694               final RefactoringEventData beforeData = new RefactoringEventData();
695               beforeData.addElement(expr);
696               project.getMessageBus()
697                 .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData);
698
699               final PsiElement chosenAnchor =
700                 chooseAnchor(settings.isReplaceAllOccurrences(), hasWriteAccess, nonWrite, anchorStatementIfAll, anchorStatement);
701
702               variable = introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings);
703             }
704             finally {
705               final RefactoringEventData afterData = new RefactoringEventData();
706               afterData.addElement(variable);
707               project.getMessageBus()
708                 .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone(REFACTORING_ID, afterData);
709             }
710           }, REFACTORING_NAME, null);
711       }
712     };
713
714     if (!isInplaceAvailableOnDataContext) {
715       callback.pass(null);
716     }
717     else {
718       OccurrencesChooser.ReplaceChoice choice = getOccurrencesChoice();
719       if (choice != null) {
720         callback.pass(choice);
721       } else {
722         OccurrencesChooser.<PsiExpression>simpleChooser(editor).showChooser(callback, occurrencesMap);
723       }
724     }
725     return wasSucceed[0];
726   }
727   
728   protected OccurrencesChooser.ReplaceChoice getOccurrencesChoice() {
729     return null;
730   }
731
732   protected static PsiElement chooseAnchor(boolean allOccurences,
733                                            boolean hasWriteAccess,
734                                            List<PsiExpression> nonWrite,
735                                            PsiElement anchorStatementIfAll,
736                                            PsiElement anchorStatement) {
737     if (allOccurences) {
738       if (hasWriteAccess) {
739         return RefactoringUtil.getAnchorElementForMultipleExpressions(nonWrite.toArray(new PsiExpression[nonWrite.size()]), null);
740       }
741       else {
742         return anchorStatementIfAll;
743       }
744     }
745     else {
746       return anchorStatement;
747     }
748   }
749
750   protected boolean isInplaceAvailableInTestMode() {
751     return false;
752   }
753
754   private static ExpressionOccurrenceManager createOccurrenceManager(PsiExpression expr, PsiElement tempContainer) {
755     boolean skipForStatement = true;
756     final PsiForStatement forStatement = PsiTreeUtil.getParentOfType(expr, PsiForStatement.class);
757     if (forStatement != null) {
758       final VariablesProcessor variablesProcessor = new VariablesProcessor(false) {
759         @Override
760         protected boolean check(PsiVariable var, ResolveState state) {
761           return PsiTreeUtil.isAncestor(forStatement.getInitialization(), var, true);
762         }
763       };
764       PsiScopesUtil.treeWalkUp(variablesProcessor, expr, null);
765       skipForStatement = variablesProcessor.size() == 0;
766     }
767
768     PsiElement containerParent = tempContainer;
769     PsiElement lastScope = tempContainer;
770     while (true) {
771       if (containerParent instanceof PsiFile) break;
772       if (containerParent instanceof PsiMethod) break;
773       // allow to find occurrences outside lambda as we allow this for loops, ifs, etc
774       // if (containerParent instanceof PsiLambdaExpression) break;
775       if (!skipForStatement && containerParent instanceof PsiForStatement) break;
776       containerParent = containerParent.getParent();
777       if (containerParent instanceof PsiCodeBlock) {
778         lastScope = containerParent;
779       }
780     }
781
782     return new ExpressionOccurrenceManager(expr, lastScope, NotInSuperCallOccurrenceFilter.INSTANCE);
783   }
784
785   private static boolean isInJspHolderMethod(PsiExpression expr) {
786     final PsiElement parent1 = expr.getParent();
787     if (parent1 == null) {
788       return false;
789     }
790     final PsiElement parent2 = parent1.getParent();
791     if (!(parent2 instanceof JspCodeBlock)) return false;
792     final PsiElement parent3 = parent2.getParent();
793     return parent3 instanceof JspHolderMethod;
794   }
795
796   public static PsiVariable introduce(final Project project,
797                                       final PsiExpression expr,
798                                       final Editor editor,
799                                       final PsiElement anchorStatement,
800                                       final PsiExpression[] occurrences,
801                                       final IntroduceVariableSettings settings) {
802     final PsiElement container = anchorStatement.getParent();
803     PsiElement child = anchorStatement;
804     final boolean isInsideLoop = RefactoringUtil.isLoopOrIf(container);
805     if (!isInsideLoop) {
806       child = locateAnchor(child);
807       if (isFinalVariableOnLHS(expr)) {
808         child = child.getNextSibling();
809       }
810     }
811     final PsiElement anchor = child == null ? anchorStatement : child;
812
813     boolean tempDeleteSelf = false;
814     final boolean replaceSelf = settings.isReplaceLValues() || !RefactoringUtil.isAssignmentLHS(expr);
815     final PsiElement exprParent = expr.getParent();
816     if (!isInsideLoop) {
817       if (exprParent instanceof PsiExpressionStatement && anchor.equals(anchorStatement)) {
818         PsiElement parent = exprParent.getParent();
819         if (parent instanceof PsiCodeBlock ||
820             //fabrique
821             parent instanceof PsiCodeFragment) {
822           tempDeleteSelf = true;
823         }
824       }
825       tempDeleteSelf &= replaceSelf;
826     }
827     final boolean deleteSelf = tempDeleteSelf;
828     final boolean replaceLoop = isInsideLoop ? exprParent instanceof PsiExpressionStatement 
829                                              : container instanceof PsiLambdaExpression && exprParent == container;
830
831
832     final int col = editor != null ? editor.getCaretModel().getLogicalPosition().column : 0;
833     final int line = editor != null ? editor.getCaretModel().getLogicalPosition().line : 0;
834     if (deleteSelf) {
835       if (editor != null) {
836         LogicalPosition pos = new LogicalPosition(line, col);
837         editor.getCaretModel().moveToLogicalPosition(pos);
838       }
839     }
840
841     final PsiCodeBlock newDeclarationScope = PsiTreeUtil.getParentOfType(container, PsiCodeBlock.class, false);
842     final FieldConflictsResolver fieldConflictsResolver = new FieldConflictsResolver(settings.getEnteredName(), newDeclarationScope);
843     SmartPsiElementPointer<PsiVariable> pointer = ApplicationManager.getApplication().runWriteAction(new Computable<SmartPsiElementPointer<PsiVariable>> () {
844       @Override
845       public SmartPsiElementPointer<PsiVariable> compute() {
846         try {
847           PsiStatement statement = null;
848           if (!isInsideLoop && deleteSelf) {
849             statement = (PsiStatement)exprParent;
850           }
851
852           final PsiExpression expr1 = fieldConflictsResolver.fixInitializer(expr);
853           PsiExpression initializer = RefactoringUtil.unparenthesizeExpression(expr1);
854           final SmartTypePointer selectedType = SmartTypePointerManager.getInstance(project).createSmartTypePointer(
855             settings.getSelectedType());
856           initializer = simplifyVariableInitializer(initializer, selectedType.getType());
857
858           PsiType type = stripNullabilityAnnotationsFromTargetType(selectedType, project);
859           PsiDeclarationStatement declaration = JavaPsiFacade.getInstance(project).getElementFactory()
860             .createVariableDeclarationStatement(settings.getEnteredName(), type, initializer, container);
861           if (!isInsideLoop) {
862             declaration = addDeclaration(declaration, initializer);
863             LOG.assertTrue(expr1.isValid());
864             if (deleteSelf) {
865               final PsiElement lastChild = statement.getLastChild();
866               if (lastChild instanceof PsiComment) { // keep trailing comment
867                 declaration.addBefore(lastChild, null);
868               }
869               statement.delete();
870               if (editor != null) {
871                 LogicalPosition pos = new LogicalPosition(line, col);
872                 editor.getCaretModel().moveToLogicalPosition(pos);
873                 editor.getCaretModel().moveToOffset(declaration.getTextRange().getEndOffset());
874                 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
875                 editor.getSelectionModel().removeSelection();
876               }
877             }
878           }
879
880           PsiExpression ref = JavaPsiFacade.getInstance(project).getElementFactory().createExpressionFromText(settings.getEnteredName(), null);
881           if (settings.isReplaceAllOccurrences()) {
882             ArrayList<PsiElement> array = new ArrayList<>();
883             for (PsiExpression occurrence : occurrences) {
884               if (deleteSelf && occurrence.equals(expr)) continue;
885               if (occurrence.equals(expr)) {
886                 occurrence = expr1;
887               }
888               if (occurrence != null) {
889                 occurrence = RefactoringUtil.outermostParenthesizedExpression(occurrence);
890               }
891               if (settings.isReplaceLValues() || !RefactoringUtil.isAssignmentLHS(occurrence)) {
892                 array.add(replace(occurrence, ref, project));
893               }
894             }
895
896             if (!deleteSelf && replaceSelf && expr1 instanceof PsiPolyadicExpression && expr1.isValid() && !expr1.isPhysical() ) {
897               array.add(replace(expr1, ref, project));
898             }
899
900             if (editor != null) {
901               final PsiElement[] replacedOccurences = PsiUtilCore.toPsiElementArray(array);
902               highlightReplacedOccurences(project, editor, replacedOccurences);
903             }
904           } else {
905             if (!deleteSelf && replaceSelf) {
906               replace(expr1, ref, project);
907             }
908           }
909
910           declaration = (PsiDeclarationStatement) RefactoringUtil.putStatementInLoopBody(declaration, container, anchorStatement, replaceSelf && replaceLoop);
911           declaration = (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(project).shortenClassReferences(declaration);
912           PsiVariable var = (PsiVariable) declaration.getDeclaredElements()[0];
913           PsiUtil.setModifierProperty(var, PsiModifier.FINAL, settings.isDeclareFinal());
914           fieldConflictsResolver.fix();
915           return SmartPointerManager.getInstance(project).createSmartPsiElementPointer(var);
916         } catch (IncorrectOperationException e) {
917           LOG.error(e);
918         }
919         return null;
920       }
921
922       private PsiDeclarationStatement addDeclaration(PsiDeclarationStatement declaration, PsiExpression initializer) {
923         if (anchor instanceof PsiDeclarationStatement) {
924           final PsiElement[] declaredElements = ((PsiDeclarationStatement)anchor).getDeclaredElements();
925           if (declaredElements.length > 1) {
926             final int [] usedFirstVar = new int[] {-1};
927             initializer.accept(new JavaRecursiveElementWalkingVisitor() {
928               @Override
929               public void visitReferenceExpression(PsiReferenceExpression expression) {
930                 final int i = ArrayUtilRt.find(declaredElements, expression.resolve());
931                 if (i > -1) {
932                   usedFirstVar[0] = Math.max(i, usedFirstVar[0]);
933                 }
934                 super.visitReferenceExpression(expression);
935               }
936             });
937             if (usedFirstVar[0] > -1) {
938               final PsiVariable psiVariable = (PsiVariable)declaredElements[usedFirstVar[0]];
939               psiVariable.normalizeDeclaration();
940               final PsiDeclarationStatement parDeclarationStatement = PsiTreeUtil.getParentOfType(psiVariable, PsiDeclarationStatement.class);
941               return (PsiDeclarationStatement)container.addAfter(declaration, parDeclarationStatement);
942             }
943           }
944         }
945         return  (PsiDeclarationStatement) container.addBefore(declaration, anchor);
946       }
947     });
948     return pointer != null ? pointer.getElement() : null;
949   }
950
951   private static PsiType stripNullabilityAnnotationsFromTargetType(SmartTypePointer selectedType, final Project project) {
952     PsiType type = selectedType.getType();
953     if (type == null) return null;
954     final PsiAnnotation[] annotations = type.getAnnotations();
955     type = type.annotate(new TypeAnnotationProvider() {
956       @NotNull
957       @Override
958       public PsiAnnotation[] getAnnotations() {
959         final NullableNotNullManager manager = NullableNotNullManager.getInstance(project);
960         final Set<String> nullables = new HashSet<>();
961         nullables.addAll(manager.getNotNulls());
962         nullables.addAll(manager.getNullables());
963         return Arrays.stream(annotations)
964           .filter(annotation -> !nullables.contains(annotation.getQualifiedName()))
965           .toArray(PsiAnnotation[]::new);
966       }
967     });
968     return type;
969   }
970
971   private static boolean isFinalVariableOnLHS(PsiExpression expr) {
972     if (expr instanceof PsiReferenceExpression && RefactoringUtil.isAssignmentLHS(expr)) {
973       final PsiElement resolve = ((PsiReferenceExpression)expr).resolve();
974       if (resolve instanceof PsiVariable &&
975           ((PsiVariable)resolve).hasModifierProperty(PsiModifier.FINAL)) { //should be inserted after assignment
976         return true;
977       }
978     }
979     return false;
980   }
981
982   public static PsiExpression simplifyVariableInitializer(final PsiExpression initializer,
983                                                           final PsiType expectedType) {
984
985     if (initializer instanceof PsiTypeCastExpression) {
986       PsiExpression operand = ((PsiTypeCastExpression)initializer).getOperand();
987       if (operand != null) {
988         PsiType operandType = operand.getType();
989         if (operandType != null && TypeConversionUtil.isAssignable(expectedType, operandType)) {
990           return operand;
991         }
992       }
993     }
994     else if (initializer instanceof PsiNewExpression) {
995       final PsiNewExpression newExpression = (PsiNewExpression)initializer;
996       if (newExpression.getArrayInitializer() != null) {
997         return newExpression.getArrayInitializer();
998       }
999       else {
1000         final PsiExpression tryToDetectDiamondNewExpr = ((PsiVariable)JavaPsiFacade.getElementFactory(initializer.getProject())
1001           .createVariableDeclarationStatement("x", expectedType, initializer, initializer).getDeclaredElements()[0])
1002           .getInitializer();
1003         if (tryToDetectDiamondNewExpr instanceof PsiNewExpression &&
1004             PsiDiamondTypeUtil.canCollapseToDiamond((PsiNewExpression)tryToDetectDiamondNewExpr,
1005                                                     (PsiNewExpression)tryToDetectDiamondNewExpr,
1006                                                     expectedType)) {
1007           final PsiElement paramList = PsiDiamondTypeUtil
1008             .replaceExplicitWithDiamond(newExpression.getClassOrAnonymousClassReference().getParameterList());
1009           return PsiTreeUtil.getParentOfType(paramList, PsiNewExpression.class);
1010         }
1011       }
1012     }
1013     return initializer;
1014   }
1015
1016   public static PsiElement replace(final PsiExpression expr1, final PsiExpression ref, final Project project)
1017     throws IncorrectOperationException {
1018     final PsiExpression expr2;
1019     if (expr1 instanceof PsiArrayInitializerExpression &&
1020       expr1.getParent() instanceof PsiNewExpression) {
1021       expr2 = (PsiNewExpression) expr1.getParent();
1022     } else {
1023       expr2 = RefactoringUtil.outermostParenthesizedExpression(expr1);
1024     }
1025     if (expr2.isPhysical() || expr1.getUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL) != null) {
1026       return expr2.replace(ref);
1027     }
1028     else {
1029       final String prefix  = expr1.getUserData(ElementToWorkOn.PREFIX);
1030       final String suffix  = expr1.getUserData(ElementToWorkOn.SUFFIX);
1031       final PsiElement parent = expr1.getUserData(ElementToWorkOn.PARENT);
1032       final RangeMarker rangeMarker = expr1.getUserData(ElementToWorkOn.TEXT_RANGE);
1033
1034       LOG.assertTrue(parent != null, expr1);
1035       return parent.replace(createReplacement(ref.getText(), project, prefix, suffix, parent, rangeMarker, new int[1]));
1036     }
1037   }
1038
1039   private static PsiElement createReplacement(final String refText, final Project project,
1040                                                  final String prefix,
1041                                                  final String suffix,
1042                                                  final PsiElement parent, final RangeMarker rangeMarker, int[] refIdx) {
1043     String text = refText;
1044     if (parent != null) {
1045       final String allText = parent.getContainingFile().getText();
1046       final TextRange parentRange = parent.getTextRange();
1047
1048       LOG.assertTrue(parentRange.getStartOffset() <= rangeMarker.getStartOffset(), parent + "; prefix:" + prefix + "; suffix:" + suffix);
1049       String beg = allText.substring(parentRange.getStartOffset(), rangeMarker.getStartOffset());
1050       if (StringUtil.stripQuotesAroundValue(beg).trim().length() == 0 && prefix == null) beg = "";
1051
1052       LOG.assertTrue(rangeMarker.getEndOffset() <= parentRange.getEndOffset(), parent + "; prefix:" + prefix + "; suffix:" + suffix);
1053       String end = allText.substring(rangeMarker.getEndOffset(), parentRange.getEndOffset());
1054       if (StringUtil.stripQuotesAroundValue(end).trim().length() == 0 && suffix == null) end = "";
1055
1056       final String start = beg + (prefix != null ? prefix : "");
1057       refIdx[0] = start.length();
1058       text = start + refText + (suffix != null ? suffix : "") + end;
1059     }
1060     final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
1061     return parent instanceof PsiStatement ? factory.createStatementFromText(text, parent) :
1062                                             parent instanceof PsiCodeBlock ? factory.createCodeBlockFromText(text, parent) 
1063                                                                            : factory.createExpressionFromText(text, parent);
1064   }
1065
1066   private boolean parentStatementNotFound(final Project project, Editor editor) {
1067     String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
1068     showErrorMessage(project, editor, message);
1069     return false;
1070   }
1071
1072   protected boolean invokeImpl(Project project, PsiLocalVariable localVariable, Editor editor) {
1073     throw new UnsupportedOperationException();
1074   }
1075
1076   private static PsiElement locateAnchor(PsiElement child) {
1077     while (child != null) {
1078       PsiElement prev = child.getPrevSibling();
1079       if (prev instanceof PsiStatement) break;
1080       if (prev instanceof PsiJavaToken && ((PsiJavaToken)prev).getTokenType() == JavaTokenType.LBRACE) break;
1081       child = prev;
1082     }
1083
1084     while (child instanceof PsiWhiteSpace || child instanceof PsiComment) {
1085       child = child.getNextSibling();
1086     }
1087     return child;
1088   }
1089
1090   protected static void highlightReplacedOccurences(Project project, Editor editor, PsiElement[] replacedOccurences){
1091     if (editor == null) return;
1092     if (ApplicationManager.getApplication().isUnitTestMode()) return;
1093     HighlightManager highlightManager = HighlightManager.getInstance(project);
1094     EditorColorsManager colorsManager = EditorColorsManager.getInstance();
1095     TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
1096     highlightManager.addOccurrenceHighlights(editor, replacedOccurences, attributes, true, null);
1097     WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
1098   }
1099
1100   protected abstract void showErrorMessage(Project project, Editor editor, String message);
1101
1102
1103   protected boolean reportConflicts(MultiMap<PsiElement,String> conflicts, Project project, IntroduceVariableSettings settings){
1104     return false;
1105   }
1106
1107   public IntroduceVariableSettings getSettings(Project project, Editor editor,
1108                                                PsiExpression expr, PsiExpression[] occurrences,
1109                                                final TypeSelectorManagerImpl typeSelectorManager,
1110                                                boolean declareFinalIfAll,
1111                                                boolean anyAssignmentLHS,
1112                                                final InputValidator validator,
1113                                                PsiElement anchor,
1114                                                final OccurrencesChooser.ReplaceChoice replaceChoice) {
1115     final boolean replaceAll =
1116       replaceChoice == OccurrencesChooser.ReplaceChoice.ALL || replaceChoice == OccurrencesChooser.ReplaceChoice.NO_WRITE;
1117     final SuggestedNameInfo suggestedName = getSuggestedName(typeSelectorManager.getDefaultType(), expr, anchor);
1118     final String variableName = suggestedName.names.length > 0 ? suggestedName.names[0] : "";
1119     final boolean declareFinal = replaceAll && declareFinalIfAll || !anyAssignmentLHS && createFinals(project);
1120     final boolean replaceWrite = anyAssignmentLHS && replaceChoice == OccurrencesChooser.ReplaceChoice.ALL;
1121     return new IntroduceVariableSettings() {
1122       @Override
1123       public String getEnteredName() {
1124         return variableName;
1125       }
1126
1127       @Override
1128       public boolean isReplaceAllOccurrences() {
1129         return replaceAll;
1130       }
1131
1132       @Override
1133       public boolean isDeclareFinal() {
1134         return declareFinal;
1135       }
1136
1137       @Override
1138       public boolean isReplaceLValues() {
1139         return replaceWrite;
1140       }
1141
1142       @Override
1143       public PsiType getSelectedType() {
1144         final PsiType selectedType = typeSelectorManager.getTypeSelector().getSelectedType();
1145         return selectedType != null ? selectedType : typeSelectorManager.getDefaultType();
1146       }
1147
1148       @Override
1149       public boolean isOK() {
1150         return true;
1151       }
1152     };
1153   }
1154
1155   public static boolean createFinals(Project project) {
1156     final Boolean createFinals = JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_FINALS;
1157     return createFinals == null ? CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS : createFinals.booleanValue();
1158   }
1159
1160   public static boolean checkAnchorBeforeThisOrSuper(final Project project,
1161                                                      final Editor editor,
1162                                                      final PsiElement tempAnchorElement,
1163                                                      final String refactoringName,
1164                                                      final String helpID) {
1165     if (tempAnchorElement instanceof PsiExpressionStatement) {
1166       PsiExpression enclosingExpr = ((PsiExpressionStatement)tempAnchorElement).getExpression();
1167       if (enclosingExpr instanceof PsiMethodCallExpression) {
1168         PsiMethod method = ((PsiMethodCallExpression)enclosingExpr).resolveMethod();
1169         if (method != null && method.isConstructor()) {
1170           //This is either 'this' or 'super', both must be the first in the respective contructor
1171           String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("invalid.expression.context"));
1172           CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpID);
1173           return true;
1174         }
1175       }
1176     }
1177     return false;
1178   }
1179
1180   public interface Validator {
1181     boolean isOK(IntroduceVariableSettings dialog);
1182   }
1183
1184   public static void checkInLoopCondition(PsiExpression occurence, MultiMap<PsiElement, String> conflicts) {
1185     final PsiElement loopForLoopCondition = RefactoringUtil.getLoopForLoopCondition(occurence);
1186     if (loopForLoopCondition == null) return;
1187     final List<PsiVariable> referencedVariables = RefactoringUtil.collectReferencedVariables(occurence);
1188     final List<PsiVariable> modifiedInBody = new ArrayList<>();
1189     for (PsiVariable psiVariable : referencedVariables) {
1190       if (RefactoringUtil.isModifiedInScope(psiVariable, loopForLoopCondition)) {
1191         modifiedInBody.add(psiVariable);
1192       }
1193     }
1194
1195     if (!modifiedInBody.isEmpty()) {
1196       for (PsiVariable variable : modifiedInBody) {
1197         final String message = RefactoringBundle.message("is.modified.in.loop.body", RefactoringUIUtil.getDescription(variable, false));
1198         conflicts.putValue(variable, CommonRefactoringUtil.capitalize(message));
1199       }
1200       conflicts.putValue(occurence, RefactoringBundle.message("introducing.variable.may.break.code.logic"));
1201     }
1202   }
1203
1204   @Override
1205   public AbstractInplaceIntroducer getInplaceIntroducer() {
1206     return myInplaceIntroducer;
1207   }
1208 }