update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / introduceVariable / IntroduceVariableBase.java
blobfe8f467765d813bae8e3fd19205ef2cf8983ced4
1 /*
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.
17 /**
18 * Created by IntelliJ IDEA.
19 * User: dsl
20 * Date: Nov 15, 2002
21 * Time: 5:21:33 PM
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;
66 import javax.swing.*;
67 import javax.swing.event.ListSelectionEvent;
68 import javax.swing.event.ListSelectionListener;
69 import java.awt.*;
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();
85 } else {
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());
93 else {
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());
98 });
99 return;
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))) {
121 correctedOffset--;
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);
152 return expressions;
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() {
173 @Override
174 public Component getListCellRendererComponent(final JList list,
175 final Object value,
176 final int index,
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>();
195 toExtract.add(expr);
196 highlighter.highlight(expr, toExtract);
200 JBPopupFactory.getInstance().createListPopupBuilder(list)
201 .setTitle("Expressions")
202 .setMovable(false)
203 .setResizable(false)
204 .setRequestFocus(true)
205 .setItemChoosenCallback(new Runnable() {
206 public void run() {
207 callback.pass((PsiExpression)list.getSelectedValue());
210 .addListener(new JBPopupAdapter() {
211 @Override
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();
248 try {
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) {
260 text = "\"" + text;
261 } else if (startOffset > startExpressionOffset + 1){
262 prefix = "\" + ";
263 text = "\"" + text;
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) {
274 text += "\"";
275 } else if (endOffset < endExpressionOffset - 1) {
276 suffix = " + \"";
277 text += "\"";
281 boolean primitive = false;
282 if (stripped.equals("true") || stripped.equals("false")) {
283 primitive = true;
285 else {
286 try {
287 Integer.parseInt(stripped);
288 primitive = true;
290 catch (NumberFormatException e1) {
291 //then not primitive
295 if (primitive) {
296 text = stripped;
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() {
304 @Override
305 public void visitElement(final PsiElement element) {
306 if (hasErrors[0]) {
307 return;
309 super.visitElement(element);
312 @Override
313 public void visitErrorElement(final PsiErrorElement element) {
314 hasErrors[0] = true;
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())) {
337 return null;
340 catch (IncorrectOperationException e) {
341 return null;
344 return tempExpr;
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);
356 if (expr == null) {
357 String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.block.should.represent.an.expression"));
358 showErrorMessage(project, editor, message);
359 return false;
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);
369 return false;
372 if (PsiType.VOID.equals(originalType)) {
373 String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.expression.has.void.type"));
374 showErrorMessage(project, editor, message);
375 return false;
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);
394 return false;
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);
404 return false;
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);
410 return false;
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;
420 while (true) {
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;
440 break;
445 IntroduceVariableSettings settings = getSettings(project, editor, expr, occurrences, anyAssignmentLHS, declareFinalIfAll,
446 originalType,
447 new TypeSelectorManagerImpl(project, originalType, expr, occurrences),
448 new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurenceManager));
450 if (!settings.isOK()) {
451 return false;
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();
460 if (replaceAll) {
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 ||
480 //fabrique
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;
492 if (deleteSelf) {
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() {
504 public void run() {
505 try {
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);
520 if (!isInsideLoop) {
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);
528 statement.delete();
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);
540 if (replaceAll) {
541 ArrayList<PsiElement> array = new ArrayList<PsiElement>();
542 for (PsiExpression occurrence : occurrences) {
543 if (deleteSelf && occurrence.equals(expr)) continue;
544 if (occurrence.equals(expr)) {
545 occurrence = expr1;
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);
559 } else {
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) {
571 LOG.error(e);
576 CommandProcessor.getInstance().executeCommand(
577 project,
578 new Runnable() {
579 public void run() {
580 ApplicationManager.getApplication().runWriteAction(runnable);
582 }, REFACTORING_NAME, null);
583 return true;
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);
592 else {
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,
603 final String prefix,
604 final String suffix,
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);
640 return declaration;
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);
646 return false;
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;
658 child = prev;
661 while (child instanceof PsiWhiteSpace || child instanceof PsiComment) {
662 child = child.getNextSibling();
664 return child;
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);
675 @Nullable
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)) {
683 return thenBranch;
685 final PsiStatement elseBranch = ((PsiIfStatement)container).getElseBranch();
686 if (elseBranch != null && PsiTreeUtil.isAncestor(elseBranch, anchorStatement, false)) {
687 return elseBranch;
689 LOG.assertTrue(false);
691 LOG.assertTrue(false);
692 return null;
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"));