boolean (final Project project, final PsiExpression expr, @Nullable PsiElement targetContainer, @Nullable JavaReplaceChoice replaceChoice, final Editor editor) { if (expr != null) { final String errorMessage = IntroduceVariableUtil.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 (IntroduceVariableUtil.LOG.isDebugEnabled()) { IntroduceVariableUtil.LOG.debug("expression:" + expr); } if (expr == null || !expr.isPhysical()) { if (ReassignVariableUtil.reassign(editor)) return false; if (expr == null) { String message = RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("selected.block.should.represent.an.expression")); showErrorMessage(project, editor, message); return false; } } String enumInSwitchError = RefactoringUtil.checkEnumConstantInSwitchLabel(expr); if (enumInSwitchError != null) { showErrorMessage(project, editor, enumInSwitchError); return false; } final PsiType originalType = CommonJavaRefactoringUtil.getTypeByExpressionWithExpectedType(expr); if (originalType == null || LambdaUtil.notInferredType(originalType)) { String message = RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("unknown.expression.type")); showErrorMessage(project, editor, message); return false; } if (PsiTypes.voidType().equals(originalType)) { String message = RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("selected.expression.has.void.type")); showErrorMessage(project, editor, message); return false; } try { JavaPsiFacade.getElementFactory(project).createTypeElementFromText( GenericsUtil.getVariableTypeByExpressionType(originalType).getCanonicalText(), expr); } catch (IncorrectOperationException ignore) { String message = RefactoringBundle.getCannotRefactorMessage(JavaRefactoringBundle.message("unknown.expression.type")); showErrorMessage(project, editor, message); return false; } for (PsiPatternVariable variable : JavaPsiPatternUtil.getExposedPatternVariables(expr)) { if (VariableAccessUtils.getVariableReferences(variable, variable.getDeclarationScope()).stream() .anyMatch(ref -> !PsiTreeUtil.isAncestor(expr, ref, true))) { String message = RefactoringBundle.getCannotRefactorMessage( JavaRefactoringBundle.message("selected.expression.introduces.pattern.variable", variable.getName())); showErrorMessage(project, editor, message); return false; } } final PsiElement anchorStatement = getAnchor(expr); PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement); if (tempContainer == null) return false; final PsiFile file = anchorStatement.getContainingFile(); IntroduceVariableUtil.LOG.assertTrue(file != null, "expr.getContainingFile() == null"); final PsiElement nameSuggestionContext = editor == null ? null : file.findElementAt(editor.getCaretModel().getOffset()); final RefactoringSupportProvider supportProvider = LanguageRefactoringSupport.INSTANCE.forContext(expr); final boolean isInplaceAvailableOnDataContext = supportProvider != null && editor.getSettings().isVariableInplaceRenameEnabled() && supportProvider.isInplaceIntroduceAvailable(expr, nameSuggestionContext) && !isInJspHolderMethod(expr); if (isInplaceAvailableOnDataContext) { final MultiMap<PsiElement, String> conflicts = new MultiMap<>(); checkInLoopCondition(expr, conflicts); if (!conflicts.isEmpty()) { showErrorMessage(project, editor, StringUtil.join(new TreeSet<>(conflicts.values()), "<br>")); return false; } } final ExpressionOccurrenceManager occurrenceManager = createOccurrenceManager(expr, tempContainer); final PsiExpression[] occurrences = occurrenceManager.getOccurrences(); OccurrencesInfo occurrencesInfo = new OccurrencesInfo(occurrences); if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return false; final LinkedHashMap<JavaReplaceChoice, List<PsiExpression>> occurrencesMap = occurrencesInfo.buildOccurrencesMap(expr); class IntroduceVariablePass implements Consumer<JavaReplaceChoice> { boolean wasSucceed = true; @Override public void accept(JavaReplaceChoice choice) { Consumer<JavaReplaceChoice> dialogIntroduce = c -> CommandProcessor.getInstance().executeCommand(project, () -> introduce(c), getRefactoringName(), null); if (choice == null) { dialogIntroduce.accept(null); } else { SlowOperations.allowSlowOperations( () -> inplaceIntroduce(project, editor, choice, targetContainer, occurrenceManager, originalType, dialogIntroduce)); } } private void introduce(@Nullable JavaReplaceChoice choice) { 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 { boolean hasWriteAccess = occurrencesInfo.myHasWriteAccess; final InputValidator validator = new InputValidator(IntroduceVariableBase.this, project, occurrenceManager); final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, originalType, expr, occurrences); boolean inFinalContext = occurrenceManager.isInFinalContext(); final IntroduceVariableSettings settings = getSettings(project, topLevelEditor, expr, occurrences, typeSelectorManager, inFinalContext, hasWriteAccess, validator, anchorStatement, choice); if (!settings.isOK()) { wasSucceed = false; return; } JavaReplaceChoice finalChoice = settings.getReplaceChoice(); PsiExpression[] selectedOccurrences = finalChoice.filter(occurrenceManager); if (selectedOccurrences.length == 0) { showErrorMessage(project, editor, JavaRefactoringBundle.message("introduce.variable.no.matching.occurrences")); wasSucceed = false; return; } final PsiElement chosenAnchor = getAnchor(selectedOccurrences); if (chosenAnchor == null) { String text = file.getText(); String textWithOccurrences = StreamEx.of(selectedOccurrences) .map(e -> getPhysicalElement(e).getTextRange()) .flatMapToEntry(range -> EntryStream.of(range.getStartOffset(), "[", range.getEndOffset(), "]").toMap()) .sortedBy(Map.Entry::getKey) .prepend(0, "") .append(text.length(), "") .map(Function.identity()) .pairMap((prev, next) -> text.substring(prev.getKey(), next.getKey()) + next.getValue()) .joining(); IntroduceVariableUtil.LOG.error("Unable to find anchor for a new variable; selectedOccurrences.length = " + selectedOccurrences.length, new Attachment("source.java", textWithOccurrences)); return; } final RefactoringEventData beforeData = new RefactoringEventData(); beforeData.addElement(expr); project.getMessageBus() .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringStarted(REFACTORING_ID, beforeData); variable = VariableExtractor.introduce(project, expr, topLevelEditor, chosenAnchor, selectedOccurrences, settings); } finally { final RefactoringEventData afterData = new RefactoringEventData(); afterData.addElement(variable); project.getMessageBus() .syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC).refactoringDone(REFACTORING_ID, afterData); } } } final IntroduceVariablePass callback = new IntroduceVariablePass(); if (replaceChoice != null) { callback.accept(findChoice(occurrencesMap, replaceChoice)); } else if (!isInplaceAvailableOnDataContext) { callback.accept(null); } else { String title = occurrencesInfo.myChainMethodName != null && occurrences.length == 1 ? JavaRefactoringBundle.message("replace.lambda.chain.detected") : RefactoringBundle.message("replace.multiple.occurrences.found"); createOccurrencesChooser(editor).showChooser(occurrencesMap, title, callback); } return callback.wasSucceed; }