Files
openide/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java
T

1158 lines
54 KiB
Java

// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.refactoring.introduceVariable;
import com.intellij.codeInsight.CodeInsightUtil;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.unwrap.ScopeHighlighter;
import com.intellij.codeInspection.RemoveRedundantTypeArgumentsUtil;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.featureStatistics.ProductivityFeatureNames;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.lang.LanguageRefactoringSupport;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.lang.refactoring.RefactoringSupportProvider;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.codeStyle.SuggestedNameInfo;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.impl.PsiDiamondTypeUtil;
import com.intellij.psi.impl.source.jsp.jspJava.JspCodeBlock;
import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.impl.source.tree.java.ReplaceExpressionUtil;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.*;
import com.intellij.refactoring.*;
import com.intellij.refactoring.chainCall.ChainCallExtractor;
import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.refactoring.introduceField.ElementToWorkOn;
import com.intellij.refactoring.listeners.RefactoringEventData;
import com.intellij.refactoring.listeners.RefactoringEventListener;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager;
import com.intellij.refactoring.util.occurrences.NotInSuperCallOccurrenceFilter;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.*;
/**
* @author dsl
*/
public abstract class IntroduceVariableBase extends IntroduceHandlerBase {
public static class JavaReplaceChoice implements OccurrencesChooser.BaseReplaceChoice {
public static final JavaReplaceChoice NO = new JavaReplaceChoice(OccurrencesChooser.ReplaceChoice.NO);
public static final JavaReplaceChoice NO_WRITE = new JavaReplaceChoice(OccurrencesChooser.ReplaceChoice.NO_WRITE);
public static final JavaReplaceChoice ALL = new JavaReplaceChoice(OccurrencesChooser.ReplaceChoice.ALL);
public static final JavaReplaceChoice NO_CHAIN = new JavaReplaceChoice("Create variable inside current lambda", false, false, false);
public static final JavaReplaceChoice CHAIN = new JavaReplaceChoice("Extract as a separate operation", false, false, true);
public static final JavaReplaceChoice CHAIN_ALL =
new JavaReplaceChoice("Replace all occurrences and extract as a separate operation", true, true, true);
private final String myDescription;
private final boolean myAll, myMultiple, myChain;
JavaReplaceChoice(OccurrencesChooser.ReplaceChoice choice) {
this(choice.getDescription(), choice.isAll(), choice.isMultiple(), false);
}
JavaReplaceChoice(String description, boolean all, boolean multiple, boolean chain) {
myDescription = description;
myAll = all;
myMultiple = multiple;
myChain = chain;
}
public String getDescription() {
return myDescription;
}
@Override
public boolean isMultiple() {
return myMultiple;
}
@Override
public boolean isAll() {
return myAll;
}
public boolean isChain() {
return myChain;
}
@Override
public String formatDescription(int occurrencesCount) {
return MessageFormat.format(getDescription(), occurrencesCount);
}
}
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceVariable.IntroduceVariableBase");
@NonNls private static final String PREFER_STATEMENTS_OPTION = "introduce.variable.prefer.statements";
@NonNls private static final String REFACTORING_ID = "refactoring.extractVariable";
protected static final String REFACTORING_NAME = RefactoringBundle.message("introduce.variable.title");
public static final Key<Boolean> NEED_PARENTHESIS = Key.create("NEED_PARENTHESIS");
private JavaVariableInplaceIntroducer myInplaceIntroducer;
public static SuggestedNameInfo getSuggestedName(@Nullable PsiType type, @NotNull final PsiExpression expression) {
return getSuggestedName(type, expression, expression);
}
public static SuggestedNameInfo getSuggestedName(@Nullable PsiType type,
@NotNull final PsiExpression expression,
final PsiElement anchor) {
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(expression.getProject());
final SuggestedNameInfo nameInfo = codeStyleManager.suggestVariableName(VariableKind.LOCAL_VARIABLE, null, expression, type);
final String[] strings = JavaCompletionUtil
.completeVariableNameForRefactoring(codeStyleManager, type, VariableKind.LOCAL_VARIABLE, nameInfo);
final SuggestedNameInfo.Delegate delegate = new SuggestedNameInfo.Delegate(strings, nameInfo);
return codeStyleManager.suggestUniqueVariableName(delegate, anchor, true);
}
@Override
public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) {
final SelectionModel selectionModel = editor.getSelectionModel();
if (!selectionModel.hasSelection()) {
final int offset = editor.getCaretModel().getOffset();
final PsiElement[] statementsInRange = findStatementsAtOffset(editor, file, offset);
//try line selection
if (statementsInRange.length == 1 && selectLineAtCaret(offset, statementsInRange)) {
selectionModel.selectLineAtCaret();
final PsiExpression expressionInRange =
findExpressionInRange(project, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd());
if (expressionInRange == null || getErrorMessage(expressionInRange) != null) {
selectionModel.removeSelection();
}
}
if (!selectionModel.hasSelection()) {
final List<PsiExpression> expressions = ContainerUtil
.filter(collectExpressions(file, editor, offset), expression -> RefactoringUtil.getParentStatement(expression, false) != null);
if (expressions.isEmpty()) {
selectionModel.selectLineAtCaret();
} else if (!isChooserNeeded(expressions)) {
final TextRange textRange = expressions.get(0).getTextRange();
selectionModel.setSelection(textRange.getStartOffset(), textRange.getEndOffset());
}
else {
IntroduceTargetChooser.showChooser(editor, expressions,
new Pass<PsiExpression>(){
@Override
public void pass(final PsiExpression selectedValue) {
invoke(project, editor, file, selectedValue.getTextRange().getStartOffset(), selectedValue.getTextRange().getEndOffset());
}
},
new PsiExpressionTrimRenderer.RenderFunction(), "Expressions", preferredSelection(statementsInRange, expressions), ScopeHighlighter.NATURAL_RANGER);
return;
}
}
}
if (invoke(project, editor, file, selectionModel.getSelectionStart(), selectionModel.getSelectionEnd()) &&
LookupManager.getActiveLookup(editor) == null) {
selectionModel.removeSelection();
}
}
public static boolean isChooserNeeded(List<? extends PsiExpression> expressions) {
if (expressions.size() == 1) {
final PsiExpression expression = expressions.get(0);
return expression instanceof PsiNewExpression && ((PsiNewExpression)expression).getAnonymousClass() != null;
}
return true;
}
public static boolean selectLineAtCaret(int offset, PsiElement[] statementsInRange) {
TextRange range = statementsInRange[0].getTextRange();
if (statementsInRange[0] instanceof PsiExpressionStatement) {
range = ((PsiExpressionStatement)statementsInRange[0]).getExpression().getTextRange();
}
return range.getStartOffset() > offset ||
range.getEndOffset() <= offset ||
isPreferStatements();
}
public static int preferredSelection(PsiElement[] statementsInRange, List<? extends PsiExpression> expressions) {
int selection;
if (statementsInRange.length == 1 &&
statementsInRange[0] instanceof PsiExpressionStatement &&
PsiUtilCore.hasErrorElementChild(statementsInRange[0])) {
selection = expressions.indexOf(((PsiExpressionStatement)statementsInRange[0]).getExpression());
} else {
PsiExpression expression = expressions.get(0);
if (expression instanceof PsiReferenceExpression && ((PsiReferenceExpression)expression).resolve() instanceof PsiLocalVariable) {
selection = 1;
}
else {
selection = -1;
}
}
return selection;
}
public static boolean isPreferStatements() {
return Boolean.valueOf(PropertiesComponent.getInstance().getBoolean(PREFER_STATEMENTS_OPTION)) || Registry.is(PREFER_STATEMENTS_OPTION, false);
}
public static List<PsiExpression> collectExpressions(final PsiFile file,
final Editor editor,
final int offset) {
return collectExpressions(file, editor, offset, false);
}
public static List<PsiExpression> collectExpressions(final PsiFile file,
final Editor editor,
final int offset,
boolean acceptVoid) {
return collectExpressions(file, editor.getDocument(), offset, acceptVoid);
}
public static List<PsiExpression> collectExpressions(final PsiFile file,
final Document document,
final int offset,
boolean acceptVoid) {
CharSequence text = document.getCharsSequence();
int correctedOffset = offset;
int textLength = document.getTextLength();
if (offset >= textLength) {
correctedOffset = textLength - 1;
}
else if (!Character.isJavaIdentifierPart(text.charAt(offset))) {
correctedOffset--;
}
if (correctedOffset < 0) {
correctedOffset = offset;
}
else if (!Character.isJavaIdentifierPart(text.charAt(correctedOffset))) {
if (text.charAt(correctedOffset) == ';') {//initially caret on the end of line
correctedOffset--;
}
if (correctedOffset < 0 || text.charAt(correctedOffset) != ')' && text.charAt(correctedOffset) != '.') {
correctedOffset = offset;
}
}
final PsiElement elementAtCaret = file.findElementAt(correctedOffset);
final List<PsiExpression> expressions = new ArrayList<>();
/*for (PsiElement element : statementsInRange) {
if (element instanceof PsiExpressionStatement) {
final PsiExpression expression = ((PsiExpressionStatement)element).getExpression();
if (expression.getType() != PsiType.VOID) {
expressions.add(expression);
}
}
}*/
PsiExpression expression = PsiTreeUtil.getParentOfType(elementAtCaret, PsiExpression.class);
while (expression != null) {
if (!expressions.contains(expression) && !(expression instanceof PsiParenthesizedExpression) && !(expression instanceof PsiSuperExpression) &&
(acceptVoid || !PsiType.VOID.equals(expression.getType()))) {
if (expression instanceof PsiMethodReferenceExpression) {
expressions.add(expression);
}
else if (!(expression instanceof PsiAssignmentExpression)) {
if (!(expression instanceof PsiReferenceExpression)) {
expressions.add(expression);
}
else {
if (!(expression.getParent() instanceof PsiMethodCallExpression)) {
final PsiElement resolve = ((PsiReferenceExpression)expression).resolve();
if (!(resolve instanceof PsiClass) && !(resolve instanceof PsiPackage)) {
expressions.add(expression);
}
}
}
}
}
expression = PsiTreeUtil.getParentOfType(expression, PsiExpression.class);
}
return expressions;
}
public static PsiElement[] findStatementsAtOffset(final Editor editor, final PsiFile file, final int offset) {
final Document document = editor.getDocument();
final int lineNumber = document.getLineNumber(offset);
final int lineStart = document.getLineStartOffset(lineNumber);
final int lineEnd = document.getLineEndOffset(lineNumber);
return CodeInsightUtil.findStatementsInRange(file, lineStart, lineEnd);
}
private boolean invoke(final Project project, final Editor editor, PsiFile file, int startOffset, int endOffset) {
FeatureUsageTracker.getInstance().triggerFeatureUsed(ProductivityFeatureNames.REFACTORING_INTRODUCE_VARIABLE);
PsiDocumentManager.getInstance(project).commitAllDocuments();
return invokeImpl(project, findExpressionInRange(project, file, startOffset, endOffset), editor);
}
private static PsiExpression findExpressionInRange(Project project, PsiFile file, int startOffset, int endOffset) {
PsiExpression tempExpr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset);
if (tempExpr == null) {
PsiElement[] statements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
if (statements.length == 1) {
if (statements[0] instanceof PsiExpressionStatement) {
tempExpr = ((PsiExpressionStatement) statements[0]).getExpression();
} else if (statements[0] instanceof PsiReturnStatement) {
tempExpr = ((PsiReturnStatement)statements[0]).getReturnValue();
}
}
}
if (tempExpr == null) {
tempExpr = getSelectedExpression(project, file, startOffset, endOffset);
}
return tempExpr;
}
/**
* @return can return NotNull value although extraction will fail: reason could be retrieved from {@link #getErrorMessage(PsiExpression)}
*/
public static PsiExpression getSelectedExpression(final Project project, PsiFile file, int startOffset, int endOffset) {
final InjectedLanguageManager injectedLanguageManager = InjectedLanguageManager.getInstance(project);
PsiElement elementAtStart = file.findElementAt(startOffset);
if (elementAtStart == null || elementAtStart instanceof PsiWhiteSpace || elementAtStart instanceof PsiComment) {
final PsiElement element = PsiTreeUtil.skipWhitespacesAndCommentsForward(elementAtStart);
if (element != null) {
startOffset = element.getTextOffset();
elementAtStart = file.findElementAt(startOffset);
}
if (elementAtStart == null) {
if (injectedLanguageManager.isInjectedFragment(file)) {
return getSelectionFromInjectedHost(project, file, injectedLanguageManager, startOffset, endOffset);
} else {
return null;
}
}
startOffset = elementAtStart.getTextOffset();
}
PsiElement elementAtEnd = file.findElementAt(endOffset - 1);
if (elementAtEnd == null || elementAtEnd instanceof PsiWhiteSpace || elementAtEnd instanceof PsiComment) {
elementAtEnd = PsiTreeUtil.skipWhitespacesAndCommentsBackward(elementAtEnd);
if (elementAtEnd == null) return null;
endOffset = elementAtEnd.getTextRange().getEndOffset();
}
if (endOffset <= startOffset) return null;
PsiElement elementAt = PsiTreeUtil.findCommonParent(elementAtStart, elementAtEnd);
if (elementAt instanceof PsiExpressionStatement) {
return ((PsiExpressionStatement)elementAt).getExpression();
}
final PsiExpression containingExpression = PsiTreeUtil.getParentOfType(elementAt, PsiExpression.class, false);
if (containingExpression != null && containingExpression == elementAtEnd && startOffset == containingExpression.getTextOffset()) {
return containingExpression;
}
if (containingExpression == null || containingExpression instanceof PsiLambdaExpression) {
if (injectedLanguageManager.isInjectedFragment(file)) {
return getSelectionFromInjectedHost(project, file, injectedLanguageManager, startOffset, endOffset);
}
elementAt = null;
}
final PsiLiteralExpression literalExpression = PsiTreeUtil.getParentOfType(elementAt, PsiLiteralExpression.class);
final PsiLiteralExpression startLiteralExpression = PsiTreeUtil.getParentOfType(elementAtStart, PsiLiteralExpression.class);
final PsiLiteralExpression endLiteralExpression = PsiTreeUtil.getParentOfType(file.findElementAt(endOffset), PsiLiteralExpression.class);
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project);
String text = null;
PsiExpression tempExpr;
try {
text = file.getText().subSequence(startOffset, endOffset).toString();
String prefix = null;
if (startLiteralExpression != null) {
final int startExpressionOffset = startLiteralExpression.getTextOffset();
if (startOffset == startExpressionOffset + 1) {
text = "\"" + text;
} else if (startOffset > startExpressionOffset + 1){
prefix = "\" + ";
text = "\"" + text;
}
}
String suffix = null;
if (endLiteralExpression != null) {
final int endExpressionOffset = endLiteralExpression.getTextOffset() + endLiteralExpression.getTextLength();
if (endOffset == endExpressionOffset - 1) {
text += "\"";
} else if (endOffset < endExpressionOffset - 1) {
suffix = " + \"";
text += "\"";
}
}
if (literalExpression != null && text.equals(literalExpression.getText())) return literalExpression;
final PsiElement parent = literalExpression != null ? literalExpression : elementAt;
tempExpr = elementFactory.createExpressionFromText(text, parent);
final boolean [] hasErrors = new boolean[1];
final JavaRecursiveElementWalkingVisitor errorsVisitor = new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitElement(final PsiElement element) {
if (hasErrors[0]) {
return;
}
super.visitElement(element);
}
@Override
public void visitErrorElement(final PsiErrorElement element) {
hasErrors[0] = true;
}
};
tempExpr.accept(errorsVisitor);
if (hasErrors[0]) return null;
tempExpr.putUserData(ElementToWorkOn.PREFIX, prefix);
tempExpr.putUserData(ElementToWorkOn.SUFFIX, suffix);
final RangeMarker rangeMarker =
FileDocumentManager.getInstance().getDocument(file.getVirtualFile()).createRangeMarker(startOffset, endOffset);
tempExpr.putUserData(ElementToWorkOn.TEXT_RANGE, rangeMarker);
if (parent != null) {
tempExpr.putUserData(ElementToWorkOn.PARENT, parent);
}
else {
PsiErrorElement errorElement = elementAtStart instanceof PsiErrorElement
? (PsiErrorElement)elementAtStart
: PsiTreeUtil.getNextSiblingOfType(elementAtStart, PsiErrorElement.class);
if (errorElement == null) {
errorElement = PsiTreeUtil.getParentOfType(elementAtStart, PsiErrorElement.class);
}
if (errorElement == null) return null;
if (!(errorElement.getParent() instanceof PsiClass)) return null;
tempExpr.putUserData(ElementToWorkOn.PARENT, errorElement);
tempExpr.putUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK, Boolean.TRUE);
}
final String fakeInitializer = "intellijidearulezzz";
final int[] refIdx = new int[1];
final PsiElement toBeExpression = createReplacement(fakeInitializer, project, prefix, suffix, parent, rangeMarker, refIdx);
toBeExpression.accept(errorsVisitor);
if (hasErrors[0]) return null;
if (literalExpression != null && toBeExpression instanceof PsiExpression) {
PsiType type = ((PsiExpression)toBeExpression).getType();
if (type != null && !type.equals(literalExpression.getType())) {
return null;
}
}
else if (containingExpression != null && !Comparing.equal(containingExpression.getType(), tempExpr.getType())){
return null;
}
final PsiReferenceExpression refExpr = PsiTreeUtil.getParentOfType(toBeExpression.findElementAt(refIdx[0]), PsiReferenceExpression.class);
if (refExpr == null) return null;
if (toBeExpression == refExpr && refIdx[0] > 0) {
return null;
}
if (ReplaceExpressionUtil.isNeedParenthesis(refExpr.getNode(), tempExpr.getNode())) {
tempExpr.putCopyableUserData(NEED_PARENTHESIS, Boolean.TRUE);
return tempExpr;
}
}
catch (IncorrectOperationException e) {
if (elementAt instanceof PsiExpressionList) {
final PsiElement parent = elementAt.getParent();
return parent instanceof PsiCallExpression ? createArrayCreationExpression(text, startOffset, endOffset, (PsiCallExpression)parent) : null;
}
return null;
}
return tempExpr;
}
private static PsiExpression getSelectionFromInjectedHost(Project project,
PsiFile file,
InjectedLanguageManager injectedLanguageManager, int startOffset, int endOffset) {
final PsiLanguageInjectionHost injectionHost = injectedLanguageManager.getInjectionHost(file);
return getSelectedExpression(project, injectionHost.getContainingFile(), injectedLanguageManager.injectedToHost(file, startOffset), injectedLanguageManager.injectedToHost(file, endOffset));
}
@Nullable
public static String getErrorMessage(PsiExpression expr) {
final Boolean needParenthesis = expr.getCopyableUserData(NEED_PARENTHESIS);
if (needParenthesis != null && needParenthesis.booleanValue()) {
return "Extracting selected expression would change the semantic of the whole expression.";
}
return null;
}
private static PsiExpression createArrayCreationExpression(String text, int startOffset, int endOffset, PsiCallExpression parent) {
if (text == null || parent == null) return null;
if (text.contains(",")) {
final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(parent.getProject());
final JavaResolveResult resolveResult = parent.resolveMethodGenerics();
final PsiMethod psiMethod = (PsiMethod)resolveResult.getElement();
if (psiMethod == null || !psiMethod.isVarArgs()) return null;
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
final PsiParameter varargParameter = parameters[parameters.length - 1];
final PsiType type = varargParameter.getType();
LOG.assertTrue(type instanceof PsiEllipsisType);
final PsiArrayType psiType = (PsiArrayType)((PsiEllipsisType)type).toArrayType();
final PsiExpression[] args = parent.getArgumentList().getExpressions();
final PsiSubstitutor psiSubstitutor = resolveResult.getSubstitutor();
if (args.length < parameters.length || startOffset < args[parameters.length - 1].getTextRange().getStartOffset()) return null;
final PsiFile containingFile = parent.getContainingFile();
PsiElement startElement = containingFile.findElementAt(startOffset);
while (startElement != null && startElement.getParent() != parent.getArgumentList()) {
startElement = startElement.getParent();
}
if (!(startElement instanceof PsiExpression) || startOffset > startElement.getTextOffset()) return null;
PsiElement endElement = containingFile.findElementAt(endOffset - 1);
while (endElement != null && endElement.getParent() != parent.getArgumentList()) {
endElement = endElement.getParent();
}
if (!(endElement instanceof PsiExpression) || endOffset < endElement.getTextRange().getEndOffset()) return null;
final PsiType componentType = TypeConversionUtil.erasure(psiSubstitutor.substitute(psiType.getComponentType()));
try {
final PsiExpression expressionFromText =
elementFactory.createExpressionFromText("new " + componentType.getCanonicalText() + "[]{" + text + "}", parent);
final RangeMarker rangeMarker =
FileDocumentManager.getInstance().getDocument(containingFile.getVirtualFile()).createRangeMarker(startOffset, endOffset);
expressionFromText.putUserData(ElementToWorkOn.TEXT_RANGE, rangeMarker);
expressionFromText.putUserData(ElementToWorkOn.PARENT, parent);
return expressionFromText;
}
catch (IncorrectOperationException e) {
return null;
}
}
return null;
}
@Override
protected boolean invokeImpl(final Project project, final PsiExpression expr,
final Editor editor) {
if (expr != null) {
final String errorMessage = getErrorMessage(expr);
if (errorMessage != null) {
showErrorMessage(project, editor, RefactoringBundle.getCannotRefactorMessage(errorMessage));
return false;
}
}
if (expr != null && expr.getParent() instanceof PsiExpressionStatement) {
FeatureUsageTracker.getInstance().triggerFeatureUsed("refactoring.introduceVariable.incompleteStatement");
}
if (LOG.isDebugEnabled()) {
LOG.debug("expression:" + expr);
}
if (expr == null || !expr.isPhysical()) {
if (ReassignVariableUtil.reassign(editor)) return false;
if (expr == null) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.block.should.represent.an.expression"));
showErrorMessage(project, editor, message);
return false;
}
}
final PsiType originalType = RefactoringUtil.getTypeByExpressionWithExpectedType(expr);
if (originalType == null || LambdaUtil.notInferredType(originalType)) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("unknown.expression.type"));
showErrorMessage(project, editor, message);
return false;
}
if (PsiType.VOID.equals(originalType)) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.expression.has.void.type"));
showErrorMessage(project, editor, message);
return false;
}
final PsiElement physicalElement = expr.getUserData(ElementToWorkOn.PARENT);
final PsiElement anchorStatement = RefactoringUtil.getParentStatement(physicalElement != null ? physicalElement : expr, false);
PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement);
if (tempContainer == null) return false;
final PsiFile file = anchorStatement.getContainingFile();
LOG.assertTrue(file != null, "expr.getContainingFile() == null");
final PsiElement nameSuggestionContext = editor == null ? null : file.findElementAt(editor.getCaretModel().getOffset());
final RefactoringSupportProvider supportProvider = LanguageRefactoringSupport.INSTANCE.forLanguage(expr.getLanguage());
final boolean isInplaceAvailableOnDataContext =
supportProvider != null &&
editor.getSettings().isVariableInplaceRenameEnabled() &&
supportProvider.isInplaceIntroduceAvailable(expr, nameSuggestionContext) &&
(!ApplicationManager.getApplication().isUnitTestMode() || isInplaceAvailableInTestMode()) &&
!isInJspHolderMethod(expr);
if (isInplaceAvailableOnDataContext) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<>();
checkInLoopCondition(expr, conflicts);
if (!conflicts.isEmpty()) {
showErrorMessage(project, editor, StringUtil.join(conflicts.values(), "<br>"));
return false;
}
}
final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, tempContainer);
final PsiExpression[] occurrences = occurrenceManager.getOccurrences();
final PsiElement anchorStatementIfAll = occurrenceManager.getAnchorStatementForAll();
OccurrencesInfo occurrencesInfo = new OccurrencesInfo(occurrences);
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return false;
final LinkedHashMap<JavaReplaceChoice, List<PsiExpression>> occurrencesMap = occurrencesInfo.buildOccurrencesMap(expr);
final boolean inFinalContext = occurrenceManager.isInFinalContext();
final InputValidator validator = new InputValidator(this, project, anchorStatementIfAll, anchorStatement, occurrenceManager);
final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences);
final boolean[] wasSucceed = new boolean[]{true};
final Pass<JavaReplaceChoice> callback = new Pass<JavaReplaceChoice>() {
@Override
public void pass(final JavaReplaceChoice choice) {
boolean hasWriteAccess = occurrencesInfo.myHasWriteAccess;
List<PsiExpression> nonWrite = occurrencesInfo.myNonWrite;
if (choice != null) {
final boolean noWriteChoice = choice == JavaReplaceChoice.NO_WRITE;
final boolean allChoice = choice.isAll();
final boolean replaceAll = allChoice || noWriteChoice;
typeSelectorManager.setAllOccurrences(replaceAll);
final PsiElement chosenAnchor = chooseAnchor(replaceAll, noWriteChoice, nonWrite, anchorStatementIfAll, anchorStatement);
final IntroduceVariableSettings settings =
getSettings(project, editor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, chosenAnchor,
choice);
final boolean cantChangeFinalModifier = (hasWriteAccess || inFinalContext) && allChoice;
PsiExpression[] allOccurrences = Arrays.stream(occurrences)
.filter(occurrence -> allChoice || (noWriteChoice && !PsiUtil.isAccessedForWriting(occurrence)) || expr.equals(occurrence))
.toArray(PsiExpression[]::new);
if (choice.isChain()) {
myInplaceIntroducer = new ChainCallInplaceIntroducer(project,
settings,
chosenAnchor,
editor, expr,
allOccurrences,
typeSelectorManager,
REFACTORING_NAME);
}
else {
myInplaceIntroducer = new JavaVariableInplaceIntroducer(project,
settings,
chosenAnchor,
editor, expr, cantChangeFinalModifier,
allOccurrences,
typeSelectorManager,
REFACTORING_NAME);
}
if (myInplaceIntroducer.startInplaceIntroduceTemplate()) {
return;
}
}
CommandProcessor.getInstance().executeCommand(
project,
() -> {
if (!anchorStatement.isValid()) {
return;
}
final Editor topLevelEditor ;
if (!InjectedLanguageManager.getInstance(project).isInjectedFragment(anchorStatement.getContainingFile())) {
topLevelEditor = InjectedLanguageUtil.getTopLevelEditor(editor);
} else {
topLevelEditor = editor;
}
PsiVariable variable = null;
try {
final IntroduceVariableSettings settings =
getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator,
anchorStatement, choice);
if (!settings.isOK()) {
wasSucceed[0] = false;
return;
}
final RefactoringEventData beforeData = new RefactoringEventData();
beforeData.addElement(expr);
project.getMessageBus()
.syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData);
final PsiElement chosenAnchor =
chooseAnchor(settings.isReplaceAllOccurrences(), hasWriteAccess, nonWrite, anchorStatementIfAll, anchorStatement);
variable = VariableExtractor.introduce(project, expr, topLevelEditor, chosenAnchor, occurrences, settings);
}
finally {
final RefactoringEventData afterData = new RefactoringEventData();
afterData.addElement(variable);
project.getMessageBus()
.syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone(REFACTORING_ID, afterData);
}
}, REFACTORING_NAME, null);
}
};
if (!isInplaceAvailableOnDataContext) {
callback.pass(null);
}
else {
JavaReplaceChoice choice = getOccurrencesChoice();
if (choice != null) {
callback.pass(choice);
} else {
String title = occurrencesInfo.myChainMethodName != null && occurrences.length == 1
? "Lambda chain detected"
: OccurrencesChooser.DEFAULT_CHOOSER_TITLE;
OccurrencesChooser.<PsiExpression>simpleChooser(editor).showChooser(callback, occurrencesMap, title);
}
}
return wasSucceed[0];
}
protected PsiElement checkAnchorStatement(Project project, Editor editor, PsiElement anchorStatement) {
if (anchorStatement == null) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return null;
}
if (checkAnchorBeforeThisOrSuper(project, editor, anchorStatement, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE)) return null;
final PsiElement tempContainer = anchorStatement.getParent();
if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) {
String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME);
showErrorMessage(project, editor, message);
return null;
}
return tempContainer;
}
protected JavaReplaceChoice getOccurrencesChoice() {
return null;
}
protected static PsiElement chooseAnchor(boolean allOccurrences,
boolean hasWriteAccess,
List<PsiExpression> nonWrite,
PsiElement anchorStatementIfAll,
PsiElement anchorStatement) {
if (allOccurrences) {
if (hasWriteAccess) {
return RefactoringUtil.getAnchorElementForMultipleExpressions(nonWrite.toArray(PsiExpression.EMPTY_ARRAY), null);
}
else {
return anchorStatementIfAll;
}
}
else {
return anchorStatement;
}
}
protected boolean isInplaceAvailableInTestMode() {
return false;
}
private static ExpressionOccurrenceManager createOccurrenceManager(PsiExpression expr, PsiElement tempContainer) {
Set<PsiVariable> vars = new HashSet<>();
SyntaxTraverser.psiTraverser().withRoot(expr)
.filter(element -> element instanceof PsiReferenceExpression)
.forEach(element -> {
final PsiElement resolve = ((PsiReferenceExpression)element).resolve();
if (resolve instanceof PsiVariable) {
vars.add((PsiVariable)resolve);
}
});
PsiElement containerParent = tempContainer;
PsiElement lastScope = tempContainer;
while (true) {
if (containerParent instanceof PsiFile) break;
if (containerParent instanceof PsiMethod) {
PsiClass containingClass = ((PsiMethod)containerParent).getContainingClass();
if (containingClass == null || !PsiUtil.isLocalOrAnonymousClass(containingClass)) break;
if (vars.stream().anyMatch(variable -> PsiTreeUtil.isAncestor(containingClass, variable, true))) {
break;
}
}
if (containerParent instanceof PsiLambdaExpression) {
PsiParameter[] parameters = ((PsiLambdaExpression)containerParent).getParameterList().getParameters();
if (Arrays.stream(parameters).anyMatch(vars::contains)) {
break;
}
}
if (containerParent instanceof PsiForStatement) {
PsiForStatement forStatement = (PsiForStatement)containerParent;
if (vars.stream().anyMatch(variable -> PsiTreeUtil.isAncestor(forStatement.getInitialization(), variable, true))) {
break;
}
}
containerParent = containerParent.getParent();
if (containerParent instanceof PsiCodeBlock) {
lastScope = containerParent;
}
}
return new ExpressionOccurrenceManager(expr, lastScope, NotInSuperCallOccurrenceFilter.INSTANCE);
}
private static boolean isInJspHolderMethod(PsiExpression expr) {
final PsiElement parent1 = expr.getParent();
if (parent1 == null) {
return false;
}
final PsiElement parent2 = parent1.getParent();
if (!(parent2 instanceof JspCodeBlock)) return false;
final PsiElement parent3 = parent2.getParent();
return parent3 instanceof JspHolderMethod;
}
static boolean isFinalVariableOnLHS(PsiExpression expr) {
if (expr instanceof PsiReferenceExpression && RefactoringUtil.isAssignmentLHS(expr)) {
final PsiElement resolve = ((PsiReferenceExpression)expr).resolve();
if (resolve instanceof PsiVariable &&
((PsiVariable)resolve).hasModifierProperty(PsiModifier.FINAL)) { //should be inserted after assignment
return true;
}
}
return false;
}
public static PsiExpression simplifyVariableInitializer(final PsiExpression initializer,
final PsiType expectedType) {
return simplifyVariableInitializer(initializer, expectedType, true);
}
public static PsiExpression simplifyVariableInitializer(final PsiExpression initializer,
final PsiType expectedType,
final boolean inDeclaration) {
if (initializer instanceof PsiTypeCastExpression) {
PsiExpression operand = ((PsiTypeCastExpression)initializer).getOperand();
if (operand != null) {
PsiType operandType = operand.getType();
if (operandType != null && TypeConversionUtil.isAssignable(expectedType, operandType)) {
return operand;
}
}
}
else if (initializer instanceof PsiNewExpression) {
final PsiNewExpression newExpression = (PsiNewExpression)initializer;
if (newExpression.getArrayInitializer() != null) {
if (inDeclaration) {
return newExpression.getArrayInitializer();
}
}
else {
final PsiExpression tryToDetectDiamondNewExpr = ((PsiVariable)JavaPsiFacade.getElementFactory(initializer.getProject())
.createVariableDeclarationStatement("x", expectedType, initializer, initializer).getDeclaredElements()[0])
.getInitializer();
if (tryToDetectDiamondNewExpr instanceof PsiNewExpression &&
PsiDiamondTypeUtil.canCollapseToDiamond((PsiNewExpression)tryToDetectDiamondNewExpr,
(PsiNewExpression)tryToDetectDiamondNewExpr,
expectedType)) {
final PsiElement paramList = RemoveRedundantTypeArgumentsUtil
.replaceExplicitWithDiamond(newExpression.getClassOrAnonymousClassReference().getParameterList());
return PsiTreeUtil.getParentOfType(paramList, PsiNewExpression.class);
}
}
}
return initializer;
}
public static PsiElement replace(final PsiExpression expr1, final PsiExpression ref, final Project project)
throws IncorrectOperationException {
final PsiExpression expr2;
if (expr1 instanceof PsiArrayInitializerExpression &&
expr1.getParent() instanceof PsiNewExpression) {
expr2 = (PsiNewExpression) expr1.getParent();
} else {
expr2 = RefactoringUtil.outermostParenthesizedExpression(expr1);
}
if (expr2.isPhysical() || expr1.getUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL) != null) {
return expr2.replace(ref);
}
else {
final String prefix = expr1.getUserData(ElementToWorkOn.PREFIX);
final String suffix = expr1.getUserData(ElementToWorkOn.SUFFIX);
final PsiElement parent = expr1.getUserData(ElementToWorkOn.PARENT);
final RangeMarker rangeMarker = expr1.getUserData(ElementToWorkOn.TEXT_RANGE);
LOG.assertTrue(parent != null, expr1);
return parent.replace(createReplacement(ref.getText(), project, prefix, suffix, parent, rangeMarker, new int[1]));
}
}
private static PsiElement createReplacement(final String refText, final Project project,
final String prefix,
final String suffix,
final PsiElement parent, final RangeMarker rangeMarker, int[] refIdx) {
String text = refText;
if (parent != null) {
final String allText = parent.getContainingFile().getText();
final TextRange parentRange = parent.getTextRange();
LOG.assertTrue(parentRange.getStartOffset() <= rangeMarker.getStartOffset(), parent + "; prefix:" + prefix + "; suffix:" + suffix);
String beg = allText.substring(parentRange.getStartOffset(), rangeMarker.getStartOffset());
//noinspection SSBasedInspection (suggested replacement breaks behavior)
if (StringUtil.stripQuotesAroundValue(beg).trim().isEmpty() && prefix == null) beg = "";
LOG.assertTrue(rangeMarker.getEndOffset() <= parentRange.getEndOffset(), parent + "; prefix:" + prefix + "; suffix:" + suffix);
String end = allText.substring(rangeMarker.getEndOffset(), parentRange.getEndOffset());
//noinspection SSBasedInspection (suggested replacement breaks behavior)
if (StringUtil.stripQuotesAroundValue(end).trim().isEmpty() && suffix == null) end = "";
final String start = beg + (prefix != null ? prefix : "");
refIdx[0] = start.length();
text = start + refText + (suffix != null ? suffix : "") + end;
}
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project);
return parent instanceof PsiStatement ? factory.createStatementFromText(text, parent) :
parent instanceof PsiCodeBlock ? factory.createCodeBlockFromText(text, parent)
: factory.createExpressionFromText(text, parent);
}
@Override
protected boolean invokeImpl(Project project, PsiLocalVariable localVariable, Editor editor) {
throw new UnsupportedOperationException();
}
protected static void highlightReplacedOccurrences(Project project, Editor editor, PsiElement[] replacedOccurrences){
if (editor == null) return;
if (ApplicationManager.getApplication().isUnitTestMode()) return;
HighlightManager highlightManager = HighlightManager.getInstance(project);
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
highlightManager.addOccurrenceHighlights(editor, replacedOccurrences, attributes, true, null);
WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
}
protected abstract void showErrorMessage(Project project, Editor editor, String message);
protected boolean reportConflicts(MultiMap<PsiElement,String> conflicts, Project project, IntroduceVariableSettings settings){
return false;
}
public IntroduceVariableSettings getSettings(Project project, Editor editor,
PsiExpression expr, PsiExpression[] occurrences,
final TypeSelectorManagerImpl typeSelectorManager,
boolean declareFinalIfAll,
boolean anyAssignmentLHS,
final InputValidator validator,
PsiElement anchor,
final JavaReplaceChoice replaceChoice) {
final boolean replaceAll = replaceChoice.isMultiple();
final SuggestedNameInfo suggestedName = getSuggestedName(typeSelectorManager.getDefaultType(), expr, anchor);
final String variableName = suggestedName.names.length > 0 ? suggestedName.names[0] : "";
final boolean declareFinal = replaceAll && declareFinalIfAll || !anyAssignmentLHS && createFinals(anchor.getContainingFile());
final boolean replaceWrite = anyAssignmentLHS && replaceChoice.isAll();
return new IntroduceVariableSettings() {
@Override
public String getEnteredName() {
return variableName;
}
@Override
public boolean isReplaceAllOccurrences() {
return replaceAll;
}
@Override
public boolean isDeclareFinal() {
return declareFinal;
}
@Override
public boolean isReplaceLValues() {
return replaceWrite;
}
@Override
public PsiType getSelectedType() {
final PsiType selectedType = typeSelectorManager.getTypeSelector().getSelectedType();
return selectedType != null ? selectedType : typeSelectorManager.getDefaultType();
}
@Override
public boolean isOK() {
return true;
}
};
}
public static boolean createFinals(@NotNull PsiFile file) {
final Boolean createFinals = JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_FINALS;
return createFinals == null ?
JavaCodeStyleSettings.getInstance(file).GENERATE_FINAL_LOCALS :
createFinals.booleanValue();
}
public static boolean checkAnchorBeforeThisOrSuper(final Project project,
final Editor editor,
final PsiElement tempAnchorElement,
final String refactoringName,
final String helpID) {
if (tempAnchorElement instanceof PsiExpressionStatement) {
PsiExpression enclosingExpr = ((PsiExpressionStatement)tempAnchorElement).getExpression();
if (enclosingExpr instanceof PsiMethodCallExpression) {
PsiMethod method = ((PsiMethodCallExpression)enclosingExpr).resolveMethod();
if (method != null && method.isConstructor()) {
//This is either 'this' or 'super', both must be the first in the respective constructor
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("invalid.expression.context"));
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpID);
return true;
}
}
}
return false;
}
public interface Validator {
boolean isOK(IntroduceVariableSettings dialog);
}
public static void checkInLoopCondition(PsiExpression occurence, MultiMap<PsiElement, String> conflicts) {
final PsiElement loopForLoopCondition = RefactoringUtil.getLoopForLoopCondition(occurence);
if (loopForLoopCondition == null || loopForLoopCondition instanceof PsiWhileStatement) return;
final List<PsiVariable> referencedVariables = RefactoringUtil.collectReferencedVariables(occurence);
final List<PsiVariable> modifiedInBody = new ArrayList<>();
for (PsiVariable psiVariable : referencedVariables) {
if (RefactoringUtil.isModifiedInScope(psiVariable, loopForLoopCondition)) {
modifiedInBody.add(psiVariable);
}
}
if (!modifiedInBody.isEmpty()) {
for (PsiVariable variable : modifiedInBody) {
final String message = RefactoringBundle.message("is.modified.in.loop.body", RefactoringUIUtil.getDescription(variable, false));
conflicts.putValue(variable, CommonRefactoringUtil.capitalize(message));
}
conflicts.putValue(occurence, RefactoringBundle.message("introducing.variable.may.break.code.logic"));
}
}
@Override
public AbstractInplaceIntroducer getInplaceIntroducer() {
return myInplaceIntroducer;
}
static class OccurrencesInfo {
List<PsiExpression> myOccurrences;
List<PsiExpression> myNonWrite;
boolean myCantReplaceAll;
boolean myCantReplaceAllButWrite;
boolean myHasWriteAccess;
final String myChainMethodName;
OccurrencesInfo(PsiExpression[] occurrences) {
myOccurrences = Arrays.asList(occurrences);
myNonWrite = new ArrayList<>();
myCantReplaceAll = false;
myCantReplaceAllButWrite = false;
for (PsiExpression occurrence : myOccurrences) {
if (!RefactoringUtil.isAssignmentLHS(occurrence)) {
myNonWrite.add(occurrence);
} else if (isFinalVariableOnLHS(occurrence)) {
myCantReplaceAll = true;
} else if (!myNonWrite.isEmpty()){
myCantReplaceAllButWrite = true;
myCantReplaceAll = true;
}
}
myHasWriteAccess = myOccurrences.size() > myNonWrite.size() && myOccurrences.size() > 1;
myChainMethodName = getChainCallExtractor();
}
private String getChainCallExtractor() {
if (myHasWriteAccess || myOccurrences.isEmpty()) return null;
PsiExpression expression = myOccurrences.get(0);
// The whole lambda body selected
if (myOccurrences.size() == 1 && expression.getParent() instanceof PsiLambdaExpression) return null;
PsiElement parent = PsiTreeUtil.findCommonParent(myOccurrences);
if (parent == null) return null;
PsiType type = expression.getType();
PsiLambdaExpression lambda = PsiTreeUtil.getParentOfType(parent, PsiLambdaExpression.class, true, PsiStatement.class);
ChainCallExtractor extractor = ChainCallExtractor.findExtractor(lambda, expression, type);
if (extractor == null) return null;
PsiParameter parameter = lambda.getParameterList().getParameters()[0];
if (!ReferencesSearch.search(parameter).forEach((Processor<PsiReference>)ref ->
myOccurrences.stream().anyMatch(expr -> PsiTreeUtil.isAncestor(expr, ref.getElement(), false)))) {
return null;
}
return extractor.getMethodName(parameter, expression, type);
}
@NotNull
LinkedHashMap<JavaReplaceChoice, List<PsiExpression>> buildOccurrencesMap(PsiExpression expr) {
final LinkedHashMap<JavaReplaceChoice, List<PsiExpression>> occurrencesMap = ContainerUtil.newLinkedHashMap();
if (myChainMethodName != null) {
if (myOccurrences.size() > 1 && !myCantReplaceAll) {
occurrencesMap.put(JavaReplaceChoice.NO, Collections.singletonList(expr));
occurrencesMap.put(JavaReplaceChoice.ALL, myOccurrences);
occurrencesMap.put(
new JavaReplaceChoice("Replace all {0} occurrences and extract as ''" + myChainMethodName + "'' operation", true, true, true),
myOccurrences);
} else {
occurrencesMap.put(JavaReplaceChoice.NO_CHAIN, Collections.singletonList(expr));
occurrencesMap.put(new JavaReplaceChoice("Extract as ''" + myChainMethodName + "'' operation", false, false, true),
Collections.singletonList(expr));
}
} else {
occurrencesMap.put(JavaReplaceChoice.NO, Collections.singletonList(expr));
if (myHasWriteAccess && !myCantReplaceAllButWrite) {
occurrencesMap.put(JavaReplaceChoice.NO_WRITE, myNonWrite);
}
if (myOccurrences.size() > 1 && !myCantReplaceAll) {
JavaReplaceChoice choice = occurrencesMap.containsKey(JavaReplaceChoice.NO_WRITE)
? new JavaReplaceChoice("Replace read and write occurrences (will change semantics!)", true, true, false)
: JavaReplaceChoice.ALL;
occurrencesMap.put(choice, myOccurrences);
}
}
return occurrencesMap;
}
}
}