2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Created by IntelliJ IDEA.
22 * To change this template use Options | File Templates.
24 package com
.intellij
.refactoring
.introduceVariable
;
26 import com
.intellij
.codeInsight
.CodeInsightUtil
;
27 import com
.intellij
.codeInsight
.unwrap
.ScopeHighlighter
;
28 import com
.intellij
.featureStatistics
.FeatureUsageTracker
;
29 import com
.intellij
.ide
.util
.PropertiesComponent
;
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
.fileEditor
.FileDocumentManager
;
36 import com
.intellij
.openapi
.project
.Project
;
37 import com
.intellij
.openapi
.ui
.popup
.JBPopupAdapter
;
38 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
39 import com
.intellij
.openapi
.ui
.popup
.LightweightWindowEvent
;
40 import com
.intellij
.openapi
.util
.Pass
;
41 import com
.intellij
.openapi
.util
.TextRange
;
42 import com
.intellij
.openapi
.util
.text
.StringUtil
;
43 import com
.intellij
.psi
.*;
44 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
45 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
46 import com
.intellij
.psi
.impl
.source
.tree
.java
.ReplaceExpressionUtil
;
47 import com
.intellij
.psi
.util
.PsiTreeUtil
;
48 import com
.intellij
.psi
.util
.PsiUtil
;
49 import com
.intellij
.refactoring
.IntroduceHandlerBase
;
50 import com
.intellij
.refactoring
.RefactoringActionHandler
;
51 import com
.intellij
.refactoring
.RefactoringBundle
;
52 import com
.intellij
.refactoring
.introduceField
.ElementToWorkOn
;
53 import com
.intellij
.refactoring
.ui
.TypeSelectorManagerImpl
;
54 import com
.intellij
.refactoring
.util
.CommonRefactoringUtil
;
55 import com
.intellij
.refactoring
.util
.FieldConflictsResolver
;
56 import com
.intellij
.refactoring
.util
.RefactoringUIUtil
;
57 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
58 import com
.intellij
.refactoring
.util
.occurences
.ExpressionOccurenceManager
;
59 import com
.intellij
.refactoring
.util
.occurences
.NotInSuperCallOccurenceFilter
;
60 import com
.intellij
.util
.IncorrectOperationException
;
61 import com
.intellij
.util
.containers
.MultiMap
;
62 import org
.jetbrains
.annotations
.NonNls
;
63 import org
.jetbrains
.annotations
.NotNull
;
64 import org
.jetbrains
.annotations
.Nullable
;
67 import javax
.swing
.event
.ListSelectionEvent
;
68 import javax
.swing
.event
.ListSelectionListener
;
70 import java
.util
.ArrayList
;
71 import java
.util
.List
;
73 public abstract class IntroduceVariableBase
extends IntroduceHandlerBase
implements RefactoringActionHandler
{
74 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.introduceVariable.IntroduceVariableBase");
75 private static final @NonNls String PREFER_STATEMENTS_OPTION
= "introduce.variable.prefer.statements";
77 protected static String REFACTORING_NAME
= RefactoringBundle
.message("introduce.variable.title");
79 public void invoke(@NotNull final Project project
, final Editor editor
, final PsiFile file
, DataContext dataContext
) {
80 if (!editor
.getSelectionModel().hasSelection()) {
81 final int offset
= editor
.getCaretModel().getOffset();
82 final PsiElement
[] statementsInRange
= findStatementsAtOffset(editor
, file
, offset
);
83 if (statementsInRange
.length
== 1 && (PsiUtil
.hasErrorElementChild(statementsInRange
[0]) || isPreferStatements())) {
84 editor
.getSelectionModel().selectLineAtCaret();
86 final List
<PsiExpression
> expressions
= collectExpressions(file
, editor
, offset
, statementsInRange
);
87 if (expressions
.isEmpty()) {
88 editor
.getSelectionModel().selectLineAtCaret();
89 } else if (expressions
.size() == 1) {
90 final TextRange textRange
= expressions
.get(0).getTextRange();
91 editor
.getSelectionModel().setSelection(textRange
.getStartOffset(), textRange
.getEndOffset());
94 showChooser(editor
, expressions
, new Pass
<PsiExpression
>(){
95 public void pass(final PsiExpression selectedValue
) {
96 invoke(project
, editor
, file
, selectedValue
.getTextRange().getStartOffset(), selectedValue
.getTextRange().getEndOffset());
103 if (invoke(project
, editor
, file
, editor
.getSelectionModel().getSelectionStart(), editor
.getSelectionModel().getSelectionEnd())) {
104 editor
.getSelectionModel().removeSelection();
108 public static boolean isPreferStatements() {
109 return Boolean
.valueOf(PropertiesComponent
.getInstance().getOrInit(PREFER_STATEMENTS_OPTION
, "false")).booleanValue();
112 public static List
<PsiExpression
> collectExpressions(final PsiFile file
, final Editor editor
, final int offset
, final PsiElement
... statementsInRange
) {
113 Document document
= editor
.getDocument();
114 CharSequence text
= document
.getCharsSequence();
115 int correctedOffset
= offset
;
116 int textLength
= document
.getTextLength();
117 if (offset
>= textLength
) {
118 correctedOffset
= textLength
- 1;
120 else if (!Character
.isJavaIdentifierPart(text
.charAt(offset
))) {
123 if (correctedOffset
< 0) {
124 correctedOffset
= offset
;
126 else if (!Character
.isJavaIdentifierPart(text
.charAt(correctedOffset
))) {
127 if (text
.charAt(correctedOffset
) != ')') {
128 correctedOffset
= offset
;
131 final PsiElement elementAtCaret
= file
.findElementAt(correctedOffset
);
132 final List
<PsiExpression
> expressions
= new ArrayList
<PsiExpression
>();
133 /*for (PsiElement element : statementsInRange) {
134 if (element instanceof PsiExpressionStatement) {
135 final PsiExpression expression = ((PsiExpressionStatement)element).getExpression();
136 if (expression.getType() != PsiType.VOID) {
137 expressions.add(expression);
141 PsiExpression expression
= PsiTreeUtil
.getParentOfType(elementAtCaret
, PsiExpression
.class);
142 while (expression
!= null) {
143 if (!expressions
.contains(expression
) && !(expression
instanceof PsiParenthesizedExpression
) && !(expression
instanceof PsiSuperExpression
) && expression
.getType() != PsiType
.VOID
) {
144 if (!(expression
instanceof PsiReferenceExpression
&& (expression
.getParent() instanceof PsiMethodCallExpression
||
145 ((PsiReferenceExpression
)expression
).resolve() instanceof PsiClass
))
146 && !(expression
instanceof PsiAssignmentExpression
)) {
147 expressions
.add(expression
);
150 expression
= PsiTreeUtil
.getParentOfType(expression
, PsiExpression
.class);
155 public static PsiElement
[] findStatementsAtOffset(final Editor editor
, final PsiFile file
, final int offset
) {
156 final Document document
= editor
.getDocument();
157 final int lineNumber
= document
.getLineNumber(offset
);
158 final int lineStart
= document
.getLineStartOffset(lineNumber
);
159 final int lineEnd
= document
.getLineEndOffset(lineNumber
);
161 return CodeInsightUtil
.findStatementsInRange(file
, lineStart
, lineEnd
);
164 public static void showChooser(final Editor editor
, final List
<PsiExpression
> expressions
, final Pass
<PsiExpression
> callback
) {
165 final ScopeHighlighter highlighter
= new ScopeHighlighter(editor
);
166 final DefaultListModel model
= new DefaultListModel();
167 for (PsiExpression expr
: expressions
) {
168 model
.addElement(expr
);
170 final JList list
= new JList(model
);
171 list
.setCellRenderer(new DefaultListCellRenderer() {
174 public Component
getListCellRendererComponent(final JList list
,
177 final boolean isSelected
,
178 final boolean cellHasFocus
) {
179 final Component rendererComponent
= super.getListCellRendererComponent(list
, value
, index
, isSelected
, cellHasFocus
);
181 final StringBuffer buf
= new StringBuffer();
182 ((PsiExpression
)value
).accept(new PsiExpressionTrimRenderer(buf
));
183 setText(buf
.toString());
184 return rendererComponent
;
188 list
.addListSelectionListener(new ListSelectionListener() {
189 public void valueChanged(final ListSelectionEvent e
) {
190 highlighter
.dropHighlight();
191 final int index
= list
.getSelectedIndex();
192 if (index
< 0 ) return;
193 final PsiExpression expr
= (PsiExpression
)model
.get(index
);
194 final ArrayList
<PsiElement
> toExtract
= new ArrayList
<PsiElement
>();
196 highlighter
.highlight(expr
, toExtract
);
200 JBPopupFactory
.getInstance().createListPopupBuilder(list
)
201 .setTitle("Expressions")
204 .setRequestFocus(true)
205 .setItemChoosenCallback(new Runnable() {
207 callback
.pass((PsiExpression
)list
.getSelectedValue());
210 .addListener(new JBPopupAdapter() {
212 public void onClosed(LightweightWindowEvent event
) {
213 highlighter
.dropHighlight();
216 .createPopup().showInBestPositionFor(editor
);
219 private boolean invoke(final Project project
, final Editor editor
, PsiFile file
, int startOffset
, int endOffset
) {
220 FeatureUsageTracker
.getInstance().triggerFeatureUsed("refactoring.introduceVariable");
221 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
224 PsiExpression tempExpr
= CodeInsightUtil
.findExpressionInRange(file
, startOffset
, endOffset
);
225 if (tempExpr
== null) {
226 PsiElement
[] statements
= CodeInsightUtil
.findStatementsInRange(file
, startOffset
, endOffset
);
227 if (statements
.length
== 1 && statements
[0] instanceof PsiExpressionStatement
) {
228 tempExpr
= ((PsiExpressionStatement
) statements
[0]).getExpression();
232 if (tempExpr
== null) {
233 tempExpr
= getSelectedExpression(project
, file
, startOffset
, endOffset
);
235 return invokeImpl(project
, tempExpr
, editor
);
238 public static PsiExpression
getSelectedExpression(final Project project
, final PsiFile file
, final int startOffset
, final int endOffset
) {
239 PsiExpression tempExpr
;
240 final PsiElement elementAt
= PsiTreeUtil
.findCommonParent(file
.findElementAt(startOffset
), file
.findElementAt(endOffset
- 1));
241 if (PsiTreeUtil
.getParentOfType(elementAt
, PsiExpression
.class, false) == null) return null;
242 final PsiLiteralExpression literalExpression
= PsiTreeUtil
.getParentOfType(elementAt
, PsiLiteralExpression
.class);
244 final PsiLiteralExpression startLiteralExpression
= PsiTreeUtil
.getParentOfType(file
.findElementAt(startOffset
), PsiLiteralExpression
.class);
245 final PsiLiteralExpression endLiteralExpression
= PsiTreeUtil
.getParentOfType(file
.findElementAt(endOffset
), PsiLiteralExpression
.class);
247 final PsiElementFactory elementFactory
= JavaPsiFacade
.getInstance(project
).getElementFactory();
249 String text
= file
.getText().subSequence(startOffset
, endOffset
).toString();
250 String prefix
= null;
251 String suffix
= null;
252 String stripped
= text
;
253 if (startLiteralExpression
!= null) {
254 final int startExpressionOffset
= startLiteralExpression
.getTextOffset();
255 if (startOffset
== startExpressionOffset
) {
256 if (StringUtil
.startsWithChar(text
, '\"') || StringUtil
.startsWithChar(text
, '\'')) {
257 stripped
= text
.substring(1);
259 } else if (startOffset
== startExpressionOffset
+ 1) {
261 } else if (startOffset
> startExpressionOffset
+ 1){
267 if (endLiteralExpression
!= null) {
268 final int endExpressionOffset
= endLiteralExpression
.getTextOffset() + endLiteralExpression
.getTextLength();
269 if (endOffset
== endExpressionOffset
) {
270 if (StringUtil
.endsWithChar(stripped
, '\"') || StringUtil
.endsWithChar(stripped
, '\'')) {
271 stripped
= stripped
.substring(0, stripped
.length() - 1);
273 } else if (endOffset
== endExpressionOffset
- 1) {
275 } else if (endOffset
< endExpressionOffset
- 1) {
281 boolean primitive
= false;
282 if (stripped
.equals("true") || stripped
.equals("false")) {
287 Integer
.parseInt(stripped
);
290 catch (NumberFormatException e1
) {
299 final PsiElement parent
= literalExpression
!= null ? literalExpression
: elementAt
;
300 tempExpr
= elementFactory
.createExpressionFromText(text
, parent
);
302 final boolean [] hasErrors
= new boolean[1];
303 final JavaRecursiveElementWalkingVisitor errorsVisitor
= new JavaRecursiveElementWalkingVisitor() {
305 public void visitElement(final PsiElement element
) {
309 super.visitElement(element
);
313 public void visitErrorElement(final PsiErrorElement element
) {
317 tempExpr
.accept(errorsVisitor
);
318 if (hasErrors
[0]) return null;
320 tempExpr
.putUserData(ElementToWorkOn
.PREFIX
, prefix
);
321 tempExpr
.putUserData(ElementToWorkOn
.SUFFIX
, suffix
);
323 final RangeMarker rangeMarker
=
324 FileDocumentManager
.getInstance().getDocument(file
.getVirtualFile()).createRangeMarker(startOffset
, endOffset
);
325 tempExpr
.putUserData(ElementToWorkOn
.TEXT_RANGE
, rangeMarker
);
327 tempExpr
.putUserData(ElementToWorkOn
.PARENT
, parent
);
329 final String fakeInitializer
= "intellijidearulezzz";
330 final PsiExpression toBeExpression
= createReplacement(fakeInitializer
, project
, prefix
, suffix
, parent
, rangeMarker
);
331 toBeExpression
.accept(errorsVisitor
);
332 if (hasErrors
[0]) return null;
334 final PsiReferenceExpression refExpr
= PsiTreeUtil
.getParentOfType(toBeExpression
.findElementAt(toBeExpression
.getText().indexOf(fakeInitializer
)), PsiReferenceExpression
.class);
335 assert refExpr
!= null;
336 if (ReplaceExpressionUtil
.isNeedParenthesis(refExpr
.getNode(), tempExpr
.getNode())) {
340 catch (IncorrectOperationException e
) {
347 protected boolean invokeImpl(final Project project
, final PsiExpression expr
,
348 final Editor editor
) {
349 if (expr
!= null && expr
.getParent() instanceof PsiExpressionStatement
) {
350 FeatureUsageTracker
.getInstance().triggerFeatureUsed("refactoring.introduceVariable.incompleteStatement");
352 if (LOG
.isDebugEnabled()) {
353 LOG
.debug("expression:" + expr
);
357 String message
= RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle
.message("selected.block.should.represent.an.expression"));
358 showErrorMessage(project
, editor
, message
);
362 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(project
).getElementFactory();
365 PsiType originalType
= RefactoringUtil
.getTypeByExpressionWithExpectedType(expr
);
366 if (originalType
== null) {
367 String message
= RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle
.message("unknown.expression.type"));
368 showErrorMessage(project
, editor
, message
);
372 if (PsiType
.VOID
.equals(originalType
)) {
373 String message
= RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle
.message("selected.expression.has.void.type"));
374 showErrorMessage(project
, editor
, message
);
379 final PsiElement physicalElement
= expr
.getUserData(ElementToWorkOn
.PARENT
);
381 PsiElement anchorStatement
= RefactoringUtil
.getParentStatement(physicalElement
!= null ? physicalElement
: expr
, false);
383 if (anchorStatement
== null) {
384 return parentStatementNotFound(project
, editor
);
386 if (anchorStatement
instanceof PsiExpressionStatement
) {
387 PsiExpression enclosingExpr
= ((PsiExpressionStatement
)anchorStatement
).getExpression();
388 if (enclosingExpr
instanceof PsiMethodCallExpression
) {
389 PsiMethod method
= ((PsiMethodCallExpression
)enclosingExpr
).resolveMethod();
390 if (method
!= null && method
.isConstructor()) {
391 //This is either 'this' or 'super', both must be the first in the respective contructor
392 String message
= RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle
.message("invalid.expression.context"));
393 showErrorMessage(project
, editor
, message
);
399 PsiElement tempContainer
= anchorStatement
.getParent();
401 if (!(tempContainer
instanceof PsiCodeBlock
) && !isLoopOrIf(tempContainer
)) {
402 String message
= RefactoringBundle
.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME
);
403 showErrorMessage(project
, editor
, message
);
407 if(!NotInSuperCallOccurenceFilter
.INSTANCE
.isOK(expr
)) {
408 String message
= RefactoringBundle
.getCannotRefactorMessage(RefactoringBundle
.message("cannot.introduce.variable.in.super.constructor.call"));
409 showErrorMessage(project
, editor
, message
);
413 final PsiFile file
= anchorStatement
.getContainingFile();
414 LOG
.assertTrue(file
!= null, "expr.getContainingFile() == null");
416 if (!CommonRefactoringUtil
.checkReadOnlyStatus(project
, file
)) return false;
418 PsiElement containerParent
= tempContainer
;
419 PsiElement lastScope
= tempContainer
;
421 if (containerParent
instanceof PsiFile
) break;
422 if (containerParent
instanceof PsiMethod
) break;
423 containerParent
= containerParent
.getParent();
424 if (containerParent
instanceof PsiCodeBlock
) {
425 lastScope
= containerParent
;
429 ExpressionOccurenceManager occurenceManager
= new ExpressionOccurenceManager(expr
, lastScope
,
430 NotInSuperCallOccurenceFilter
.INSTANCE
);
431 final PsiExpression
[] occurrences
= occurenceManager
.getOccurences();
432 final PsiElement anchorStatementIfAll
= occurenceManager
.getAnchorStatementForAll();
433 boolean declareFinalIfAll
= occurenceManager
.isInFinalContext();
436 boolean anyAssignmentLHS
= false;
437 for (PsiExpression occurrence
: occurrences
) {
438 if (RefactoringUtil
.isAssignmentLHS(occurrence
)) {
439 anyAssignmentLHS
= true;
445 IntroduceVariableSettings settings
= getSettings(project
, editor
, expr
, occurrences
, anyAssignmentLHS
, declareFinalIfAll
,
447 new TypeSelectorManagerImpl(project
, originalType
, expr
, occurrences
),
448 new InputValidator(this, project
, anchorStatementIfAll
, anchorStatement
, occurenceManager
));
450 if (!settings
.isOK()) {
454 final String variableName
= settings
.getEnteredName();
456 final PsiType type
= settings
.getSelectedType();
457 final boolean replaceAll
= settings
.isReplaceAllOccurrences();
458 final boolean replaceWrite
= settings
.isReplaceLValues();
459 final boolean declareFinal
= replaceAll
&& declareFinalIfAll
|| settings
.isDeclareFinal();
461 anchorStatement
= anchorStatementIfAll
;
462 tempContainer
= anchorStatement
.getParent();
465 final PsiElement container
= tempContainer
;
467 PsiElement child
= anchorStatement
;
468 if (!isLoopOrIf(container
)) {
469 child
= locateAnchor(child
);
471 final PsiElement anchor
= child
== null ? anchorStatement
: child
;
473 boolean tempDeleteSelf
= false;
474 final boolean replaceSelf
= replaceWrite
|| !RefactoringUtil
.isAssignmentLHS(expr
);
475 if (!isLoopOrIf(container
)) {
476 if (expr
.getParent() instanceof PsiExpressionStatement
&& anchor
.equals(anchorStatement
)) {
477 PsiStatement statement
= (PsiStatement
) expr
.getParent();
478 PsiElement parent
= statement
.getParent();
479 if (parent
instanceof PsiCodeBlock
||
481 parent
instanceof PsiCodeFragment
) {
482 tempDeleteSelf
= true;
485 tempDeleteSelf
&= replaceSelf
;
487 final boolean deleteSelf
= tempDeleteSelf
;
490 final int col
= editor
!= null ? editor
.getCaretModel().getLogicalPosition().column
: 0;
491 final int line
= editor
!= null ? editor
.getCaretModel().getLogicalPosition().line
: 0;
493 if (editor
!= null) {
494 LogicalPosition pos
= new LogicalPosition(line
, col
);
495 editor
.getCaretModel().moveToLogicalPosition(pos
);
499 final PsiCodeBlock newDeclarationScope
= PsiTreeUtil
.getParentOfType(container
, PsiCodeBlock
.class, false);
500 final FieldConflictsResolver fieldConflictsResolver
= new FieldConflictsResolver(variableName
, newDeclarationScope
);
502 final PsiElement finalAnchorStatement
= anchorStatement
;
503 final Runnable runnable
= new Runnable() {
506 PsiStatement statement
= null;
507 final boolean isInsideLoop
= isLoopOrIf(container
);
508 if (!isInsideLoop
&& deleteSelf
) {
509 statement
= (PsiStatement
) expr
.getParent();
511 final PsiExpression expr1
= fieldConflictsResolver
.fixInitializer(expr
);
512 PsiExpression initializer
= RefactoringUtil
.unparenthesizeExpression(expr1
);
513 if (expr1
instanceof PsiNewExpression
) {
514 final PsiNewExpression newExpression
= (PsiNewExpression
)expr1
;
515 if (newExpression
.getArrayInitializer() != null) {
516 initializer
= newExpression
.getArrayInitializer();
519 PsiDeclarationStatement declaration
= factory
.createVariableDeclarationStatement(variableName
, type
, initializer
);
521 declaration
= (PsiDeclarationStatement
) container
.addBefore(declaration
, anchor
);
522 LOG
.assertTrue(expr1
.isValid());
523 if (deleteSelf
) { // never true
524 final PsiElement lastChild
= statement
.getLastChild();
525 if (lastChild
instanceof PsiComment
) { // keep trailing comment
526 declaration
.addBefore(lastChild
, null);
529 if (editor
!= null) {
530 LogicalPosition pos
= new LogicalPosition(line
, col
);
531 editor
.getCaretModel().moveToLogicalPosition(pos
);
532 editor
.getCaretModel().moveToOffset(declaration
.getTextRange().getEndOffset());
533 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
534 editor
.getSelectionModel().removeSelection();
539 PsiExpression ref
= factory
.createExpressionFromText(variableName
, null);
541 ArrayList
<PsiElement
> array
= new ArrayList
<PsiElement
>();
542 for (PsiExpression occurrence
: occurrences
) {
543 if (deleteSelf
&& occurrence
.equals(expr
)) continue;
544 if (occurrence
.equals(expr
)) {
547 if (occurrence
!= null) {
548 occurrence
= RefactoringUtil
.outermostParenthesizedExpression(occurrence
);
550 if (replaceWrite
|| !RefactoringUtil
.isAssignmentLHS(occurrence
)) {
551 array
.add(replace(occurrence
, ref
, project
));
555 if (editor
!= null) {
556 final PsiElement
[] replacedOccurences
= array
.toArray(new PsiElement
[array
.size()]);
557 highlightReplacedOccurences(project
, editor
, replacedOccurences
);
560 if (!deleteSelf
&& replaceSelf
) {
561 replace(expr1
, ref
, project
);
565 declaration
= (PsiDeclarationStatement
) putStatementInLoopBody(declaration
, container
, finalAnchorStatement
);
566 PsiVariable var
= (PsiVariable
) declaration
.getDeclaredElements()[0];
567 PsiUtil
.setModifierProperty(var
, PsiModifier
.FINAL
, declareFinal
);
569 fieldConflictsResolver
.fix();
570 } catch (IncorrectOperationException e
) {
576 CommandProcessor
.getInstance().executeCommand(
580 ApplicationManager
.getApplication().runWriteAction(runnable
);
582 }, REFACTORING_NAME
, null);
586 public static PsiElement
replace(final PsiExpression expr1
, final PsiExpression ref
, final Project project
)
587 throws IncorrectOperationException
{
588 final PsiExpression expr2
= RefactoringUtil
.outermostParenthesizedExpression(expr1
);
589 if (expr2
.isPhysical()) {
590 return expr2
.replace(ref
);
593 final String prefix
= expr1
.getUserData(ElementToWorkOn
.PREFIX
);
594 final String suffix
= expr1
.getUserData(ElementToWorkOn
.SUFFIX
);
595 final PsiElement parent
= expr1
.getUserData(ElementToWorkOn
.PARENT
);
596 final RangeMarker rangeMarker
= expr1
.getUserData(ElementToWorkOn
.TEXT_RANGE
);
598 return parent
.replace(createReplacement(ref
.getText(), project
, prefix
, suffix
, parent
, rangeMarker
));
602 private static PsiExpression
createReplacement(final String refText
, final Project project
,
605 final PsiElement parent
, final RangeMarker rangeMarker
) {
606 final String allText
= parent
.getContainingFile().getText();
607 final TextRange parentRange
= parent
.getTextRange();
609 String beg
= allText
.substring(parentRange
.getStartOffset(), rangeMarker
.getStartOffset());
610 if (StringUtil
.stripQuotesAroundValue(beg
).trim().length() == 0 && prefix
== null) beg
= "";
612 String end
= allText
.substring(rangeMarker
.getEndOffset(), parentRange
.getEndOffset());
613 if (StringUtil
.stripQuotesAroundValue(end
).trim().length() == 0 && suffix
== null) end
= "";
615 final String text
= beg
+ (prefix
!= null ? prefix
: "") + refText
+ (suffix
!= null ? suffix
: "") + end
;
616 return JavaPsiFacade
.getInstance(project
).getElementFactory().createExpressionFromText(text
, parent
);
619 public static PsiStatement
putStatementInLoopBody(PsiStatement declaration
, PsiElement container
, PsiElement finalAnchorStatement
)
620 throws IncorrectOperationException
{
621 if(isLoopOrIf(container
)) {
622 PsiStatement loopBody
= getLoopBody(container
, finalAnchorStatement
);
623 PsiStatement loopBodyCopy
= loopBody
!= null ?
(PsiStatement
) loopBody
.copy() : null;
624 PsiBlockStatement blockStatement
= (PsiBlockStatement
)JavaPsiFacade
.getInstance(container
.getProject()).getElementFactory()
625 .createStatementFromText("{}", null);
626 blockStatement
= (PsiBlockStatement
) CodeStyleManager
.getInstance(container
.getProject()).reformat(blockStatement
);
627 final PsiElement prevSibling
= loopBody
.getPrevSibling();
628 if(prevSibling
instanceof PsiWhiteSpace
) {
629 final PsiElement pprev
= prevSibling
.getPrevSibling();
630 if (!(pprev
instanceof PsiComment
) || !((PsiComment
)pprev
).getTokenType().equals(JavaTokenType
.END_OF_LINE_COMMENT
)) {
631 prevSibling
.delete();
634 blockStatement
= (PsiBlockStatement
) loopBody
.replace(blockStatement
);
635 final PsiCodeBlock codeBlock
= blockStatement
.getCodeBlock();
636 declaration
= (PsiStatement
) codeBlock
.add(declaration
);
637 JavaCodeStyleManager
.getInstance(declaration
.getProject()).shortenClassReferences(declaration
);
638 if (loopBodyCopy
!= null) codeBlock
.add(loopBodyCopy
);
643 private boolean parentStatementNotFound(final Project project
, Editor editor
) {
644 String message
= RefactoringBundle
.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME
);
645 showErrorMessage(project
, editor
, message
);
649 protected boolean invokeImpl(Project project
, PsiLocalVariable localVariable
, Editor editor
) {
650 throw new UnsupportedOperationException();
653 private static PsiElement
locateAnchor(PsiElement child
) {
654 while (child
!= null) {
655 PsiElement prev
= child
.getPrevSibling();
656 if (prev
instanceof PsiStatement
) break;
657 if (prev
instanceof PsiJavaToken
&& ((PsiJavaToken
)prev
).getTokenType() == JavaTokenType
.LBRACE
) break;
661 while (child
instanceof PsiWhiteSpace
|| child
instanceof PsiComment
) {
662 child
= child
.getNextSibling();
667 protected abstract void highlightReplacedOccurences(Project project
, Editor editor
, PsiElement
[] replacedOccurences
);
669 protected abstract IntroduceVariableSettings
getSettings(Project project
, Editor editor
, PsiExpression expr
, final PsiElement
[] occurrences
,
670 boolean anyAssignmentLHS
, final boolean declareFinalIfAll
, final PsiType type
,
671 TypeSelectorManagerImpl typeSelectorManager
, InputValidator validator
);
673 protected abstract void showErrorMessage(Project project
, Editor editor
, String message
);
676 private static PsiStatement
getLoopBody(PsiElement container
, PsiElement anchorStatement
) {
677 if(container
instanceof PsiLoopStatement
) {
678 return ((PsiLoopStatement
) container
).getBody();
680 else if (container
instanceof PsiIfStatement
) {
681 final PsiStatement thenBranch
= ((PsiIfStatement
)container
).getThenBranch();
682 if (thenBranch
!= null && PsiTreeUtil
.isAncestor(thenBranch
, anchorStatement
, false)) {
685 final PsiStatement elseBranch
= ((PsiIfStatement
)container
).getElseBranch();
686 if (elseBranch
!= null && PsiTreeUtil
.isAncestor(elseBranch
, anchorStatement
, false)) {
689 LOG
.assertTrue(false);
691 LOG
.assertTrue(false);
696 public static boolean isLoopOrIf(PsiElement element
) {
697 return element
instanceof PsiLoopStatement
|| element
instanceof PsiIfStatement
;
700 public interface Validator
{
701 boolean isOK(IntroduceVariableSettings dialog
);
704 protected abstract boolean reportConflicts(MultiMap
<PsiElement
,String
> conflicts
, final Project project
, IntroduceVariableSettings dialog
);
707 public static void checkInLoopCondition(PsiExpression occurence
, MultiMap
<PsiElement
, String
> conflicts
) {
708 final PsiElement loopForLoopCondition
= RefactoringUtil
.getLoopForLoopCondition(occurence
);
709 if (loopForLoopCondition
== null) return;
710 final List
<PsiVariable
> referencedVariables
= RefactoringUtil
.collectReferencedVariables(occurence
);
711 final List
<PsiVariable
> modifiedInBody
= new ArrayList
<PsiVariable
>();
712 for (PsiVariable psiVariable
: referencedVariables
) {
713 if (RefactoringUtil
.isModifiedInScope(psiVariable
, loopForLoopCondition
)) {
714 modifiedInBody
.add(psiVariable
);
718 if (!modifiedInBody
.isEmpty()) {
719 for (PsiVariable variable
: modifiedInBody
) {
720 final String message
= RefactoringBundle
.message("is.modified.in.loop.body", RefactoringUIUtil
.getDescription(variable
, false));
721 conflicts
.putValue(variable
, CommonRefactoringUtil
.capitalize(message
));
723 conflicts
.putValue(occurence
, RefactoringBundle
.message("introducing.variable.may.break.code.logic"));