// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInsight.intention.impl.config;

import com.intellij.codeInsight.CodeInsightWorkspaceSettings;
import com.intellij.codeInsight.daemon.JavaErrorBundle;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
import com.intellij.codeInsight.daemon.impl.DaemonListeners;
import com.intellij.codeInsight.daemon.impl.HighlightInfoType;
import com.intellij.codeInsight.daemon.impl.SilentChangeVetoer;
import com.intellij.codeInsight.daemon.impl.analysis.IncreaseLanguageLevelFix;
import com.intellij.codeInsight.daemon.impl.analysis.UpgradeSdkFix;
import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.daemon.impl.quickfix.makefinal.MakeVarEffectivelyFinalFix;
import com.intellij.codeInsight.daemon.quickFix.CreateClassOrPackageFix;
import com.intellij.codeInsight.daemon.quickFix.CreateFieldOrPropertyFix;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInsight.intention.impl.*;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.actions.UnimplementInterfaceAction;
import com.intellij.codeInspection.dataFlow.fix.DeleteSwitchLabelFix;
import com.intellij.codeInspection.ex.EntryPointsManagerBase;
import com.intellij.codeInspection.ex.InspectionToolWrapper;
import com.intellij.codeInspection.ex.QuickFixWrapper;
import com.intellij.codeInspection.util.IntentionName;
import com.intellij.diagnostic.CoreAttachmentFactory;
import com.intellij.ide.scratch.ScratchUtil;
import com.intellij.java.JavaBundle;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.lang.java.JavaImportOptimizer;
import com.intellij.lang.java.request.CreateConstructorFromUsage;
import com.intellij.lang.java.request.CreateMethodFromUsage;
import com.intellij.modcommand.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.NonPhysicalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.ClassKind;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyMemberType;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.refactoring.JavaRefactoringActionHandlerFactory;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ThreeState;
import com.intellij.util.concurrency.ThreadingAssertions;
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread;
import com.siyeh.ig.controlflow.UnnecessaryDefaultInspection;
import com.siyeh.ig.fixes.*;
import com.siyeh.ipp.imports.ReplaceOnDemandImportIntention;
import com.siyeh.ipp.modifiers.ChangeModifierIntention;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;

import java.util.*;

public final class QuickFixFactoryImpl extends QuickFixFactory {
  private static final Logger LOG = Logger.getInstance(QuickFixFactoryImpl.class);

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createModifierListFix(@NotNull PsiModifierList modifierList,
                                                                                    @NotNull String modifier,
                                                                                    boolean shouldHave,
                                                                                    boolean showContainingClass) {
    return LocalQuickFixAndIntentionActionOnPsiElement.from(new ModifierFix(modifierList, modifier, shouldHave, showContainingClass),
                                                            modifierList.getParent());
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createModifierListFix(@NotNull PsiModifierListOwner owner,
                                                                                    final @NotNull String modifier,
                                                                                    final boolean shouldHave,
                                                                                    final boolean showContainingClass) {
    return LocalQuickFixAndIntentionActionOnPsiElement.from(new ModifierFix(owner, modifier, shouldHave, showContainingClass), owner);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createMethodReturnFix(@NotNull PsiMethod method,
                                                                                    @NotNull PsiType toReturn,
                                                                                    boolean fixWholeHierarchy) {
    return new MethodReturnTypeFix(method, toReturn, fixWholeHierarchy);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createMethodReturnFix(@NotNull PsiMethod method,
                                                                                    @NotNull PsiType toReturn,
                                                                                    boolean fixWholeHierarchy,
                                                                                    boolean suggestSuperTypes) {
    return new MethodReturnTypeFix(method, toReturn, fixWholeHierarchy, suggestSuperTypes);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createAnnotationMethodReturnFix(@NotNull PsiMethod method,
                                                                                              @NotNull PsiType toReturn,
                                                                                              boolean fromDefaultValue) {
    return new AnnotationMethodReturnTypeFix(method, toReturn, fromDefaultValue);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createImplementMethodsFix(@NotNull PsiClass aClass) {
    return new ImplementMethodsFix(aClass);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createImplementMethodsFix(@NotNull PsiElement psiElement) {
    return new ImplementMethodsFix(psiElement);
  }

  @Override
  public @NotNull IntentionAction createAssignmentToComparisonFix(@NotNull PsiAssignmentExpression expr) {
    return new ReplaceAssignmentWithComparisonFix(expr).asIntention();
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createMethodThrowsFix(@NotNull PsiMethod method,
                                                                                    @NotNull PsiClassType exceptionClass,
                                                                                    boolean shouldThrow,
                                                                                    boolean showContainingClass) {
    ModCommandAction action = shouldThrow
                              ? new MethodThrowsFix.Add(method, exceptionClass, showContainingClass)
                              : new MethodThrowsFix.Remove(method, exceptionClass, showContainingClass);
    return LocalQuickFixAndIntentionActionOnPsiElement.from(action, method);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createAddDefaultConstructorFix(@NotNull PsiClass aClass) {
    return LocalQuickFixAndIntentionActionOnPsiElement.from(new AddDefaultConstructorFix(aClass), aClass);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createMethodParameterTypeFix(@NotNull PsiMethod method,
                                                                                           int index,
                                                                                           @NotNull PsiType newType,
                                                                                           boolean fixWholeHierarchy) {
    return new MethodParameterFix(method, newType, index, fixWholeHierarchy);
  }

  @Override
  public @NotNull ModCommandAction createExtendsListFix(@NotNull PsiClass aClass,
                                                        @NotNull PsiClassType typeToExtendFrom,
                                                        boolean toAdd) {
    return new ExtendsListModCommandFix(aClass, typeToExtendFrom, toAdd);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createRemoveUnusedParameterFix(@NotNull PsiParameter parameter) {
    return new RemoveUnusedParameterFix(parameter);
  }

  @Override
  public @NotNull IntentionAction createRemoveUnusedVariableFix(@NotNull PsiVariable variable) {
    return new RemoveUnusedVariableFix(variable).asIntention();
  }

  @Override
  public @Nullable IntentionAction createCreateClassOrPackageFix(@NotNull PsiElement context,
                                                                 @NotNull String qualifiedName,
                                                                 boolean createClass,
                                                                 String superClass) {
    return CreateClassOrPackageFix.createFix(qualifiedName, context, createClass ? ClassKind.CLASS : null, superClass);
  }

  @Override
  public @Nullable IntentionAction createCreateClassOrInterfaceFix(@NotNull PsiElement context,
                                                                   @NotNull String qualifiedName,
                                                                   boolean createClass,
                                                                   String superClass) {
    return CreateClassOrPackageFix.createFix(qualifiedName, context, createClass ? ClassKind.CLASS : ClassKind.INTERFACE, superClass);
  }

  @Override
  public @NotNull IntentionAction createCreateFieldOrPropertyFix(@NotNull PsiClass aClass,
                                                                 @NotNull String name,
                                                                 @NotNull PsiType type,
                                                                 @NotNull PropertyMemberType targetMember,
                                                                 PsiAnnotation @NotNull ... annotations) {
    return new CreateFieldOrPropertyFix(aClass, name, type, targetMember, annotations).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddExceptionToCatchFix() {
    return new AddExceptionToCatchFix(true).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddExceptionToThrowsFix(@NotNull PsiElement element) {
    return new AddExceptionToThrowsFix(element).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddExceptionToThrowsFix(@NotNull PsiElement element, 
                                                                @NotNull Collection<PsiClassType> exceptionsToAdd) {
    return new AddExceptionToThrowsFix(element, exceptionsToAdd).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddExceptionFromFieldInitializerToConstructorThrowsFix(@NotNull PsiElement element) {
    return new AddExceptionFromFieldInitializerToConstructorThrowsFix(element).asIntention();
  }

  @Override
  public @NotNull IntentionAction createSurroundWithTryCatchFix(@NotNull PsiElement element) {
    return new SurroundWithTryCatchFix(element).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddExceptionToExistingCatch(@NotNull PsiElement element) {
    return new AddExceptionToExistingCatchFix(element).asIntention();
  }

  @Override
  public @NotNull IntentionAction createChangeToAppendFix(@NotNull IElementType sign,
                                                          @NotNull PsiType type,
                                                          @NotNull PsiAssignmentExpression assignment) {
    return new ChangeToAppendFix(sign, type, assignment).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddTypeCastFix(@NotNull PsiType type, @NotNull PsiExpression expression) {
    return new AddTypeCastFix(type, expression).asIntention();
  }

  @Override
  public @NotNull IntentionAction createReuseVariableDeclarationFix(@NotNull PsiLocalVariable variable) {
    return new ReuseVariableDeclarationFix(variable).asIntention();
  }

  @Override
  public @NotNull IntentionAction createNavigateToAlreadyDeclaredVariableFix(@NotNull PsiVariable variable) {
    return new NavigateToAlreadyDeclaredVariableFix(variable).asIntention();
  }

  @Override
  public @NotNull IntentionAction createNavigateToDuplicateElementFix(@NotNull NavigatablePsiElement element) {
    return new NavigateToDuplicateElementFix(element).asIntention();
  }

  @Override
  public @NotNull IntentionAction createShowDuplicateElementsFix(@NotNull List<@NotNull ? extends NavigatablePsiElement> elements) {
    return new ShowDuplicateElementsFix(elements).asIntention();
  }

  @Override
  public @NotNull IntentionAction createConvertToStringLiteralAction() {
    return new ConvertToStringLiteralAction().asIntention();
  }

  @Override
  public @NotNull IntentionAction createDeleteReturnFix(@NotNull PsiMethod method, @NotNull PsiReturnStatement returnStatement) {
    return new DeleteReturnFix(method, returnStatement).asIntention();
  }

  @Override
  public @NotNull IntentionAction createDeleteCatchFix(@NotNull PsiParameter parameter) {
    return new DeleteCatchFix(parameter).asIntention();
  }

  @Override
  public @NotNull IntentionAction createDeleteMultiCatchFix(@NotNull PsiTypeElement element) {
    return new DeleteMultiCatchFix(element).asIntention();
  }

  @Override
  public @NotNull IntentionAction createConvertSwitchToIfIntention(@NotNull PsiSwitchStatement statement) {
    return new ConvertSwitchToIfIntention(statement).asIntention();
  }

  @Override
  public @NotNull IntentionAction createNegationBroadScopeFix(@NotNull PsiPrefixExpression expr) {
    return new NegationBroadScopeFix(expr).asIntention();
  }

  @Override
  public @NotNull IntentionAction createCreateFieldFromUsageFix(@NotNull PsiReferenceExpression place) {
    return new CreateFieldFromUsageFix(place);
  }

  @Override
  public @NotNull IntentionAction createReplaceWithListAccessFix(@NotNull PsiArrayAccessExpression expression) {
    return new ReplaceWithListAccessFix(expression).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddNewArrayExpressionFix(@NotNull PsiArrayInitializerExpression expression) {
    return new AddNewArrayExpressionFix(expression).asIntention();
  }

  @Override
  public @NotNull IntentionAction createMoveCatchUpFix(@NotNull PsiCatchSection section, @NotNull PsiCatchSection section1) {
    return new MoveCatchUpFix(section, section1).asIntention();
  }

  @Override
  public @NotNull IntentionAction createRenameWrongRefFix(@NotNull PsiReferenceExpression ref) {
    return new RenameWrongRefFix(ref);
  }

  @Override
  public @NotNull IntentionAction createRemoveQualifierFix(@NotNull PsiExpression qualifier,
                                                           @NotNull PsiReferenceExpression expression,
                                                           @NotNull PsiClass resolved) {
    return new RemoveQualifierFix(expression, resolved).asIntention();
  }

  @Override
  public @NotNull IntentionAction createRemoveParameterListFix(@NotNull PsiMethod parent) {
    return new RemoveParameterListFix(parent).asIntention();
  }

  @Override
  public @NotNull IntentionAction createShowModulePropertiesFix(@NotNull PsiElement element) {
    return new ShowModulePropertiesFix(element);
  }
  @Override
  public @NotNull IntentionAction createShowModulePropertiesFix(@NotNull Module module) {
    return new ShowModulePropertiesFix(module);
  }

  @Override
  public @NotNull IntentionAction createIncreaseLanguageLevelFix(@NotNull LanguageLevel level) {
    return new IncreaseLanguageLevelFix(level);
  }

  @Override
  public @NotNull IntentionAction createUpgradeSdkFor(@NotNull LanguageLevel level) {
    return new UpgradeSdkFix(level);
  }

  @Override
  public @NotNull IntentionAction createChangeParameterClassFix(@NotNull PsiClass aClass, @NotNull PsiClassType type) {
    return new ChangeParameterClassFix(aClass, type);
  }

  @Override
  public @NotNull IntentionAction createSurroundWithArrayFix(@Nullable PsiCall methodCall, @Nullable PsiExpression expression) {
    return new SurroundWithArrayFix(methodCall, expression).asIntention();
  }

  @Override
  public @NotNull IntentionAction createImplementAbstractClassMethodsFix(@NotNull PsiElement elementToHighlight) {
    return new ImplementAbstractClassMethodsFix(elementToHighlight);
  }

  @Override
  public @NotNull IntentionAction createMoveClassToSeparateFileFix(@NotNull PsiClass aClass) {
    return new MoveClassToSeparateFileFix(aClass).asIntention();
  }

  @Override
  public @NotNull IntentionAction createRenameFileFix(@NotNull String newName) {
    return new RenameFileModCommand(newName).asIntention();
  }

  @Override
  public @Nullable IntentionAction createRenameFix(@NotNull PsiElement element) {
    PsiFile file = element.getContainingFile();
    if (file == null) return null;
    if(!element.isPhysical()) return null;
    ProblemDescriptor descriptor = new ProblemDescriptorBase(element,
                                         element,
                                         "",
                                         null,
                                         ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
                                         false,
                                         null,
                                         true,
                                         false);
    return QuickFixWrapper.wrap(descriptor, new RenameFix());
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createRenameElementFix(@NotNull PsiNamedElement element) {
    return new RenameElementFix(element);
  }

  @Override
  public @NotNull LocalQuickFixAndIntentionActionOnPsiElement createRenameElementFix(@NotNull PsiNamedElement element, @NotNull String newName) {
    return new RenameElementFix(element, newName);
  }

  @Override
  public @NotNull IntentionAction createChangeExtendsToImplementsFix(@NotNull PsiClass aClass, @NotNull PsiClassType classToExtendFrom) {
    return new ChangeExtendsToImplementsFix(aClass, classToExtendFrom).asIntention();
  }

  @Override
  public @NotNull IntentionAction createCreateConstructorMatchingSuperFix(@NotNull PsiClass aClass) {
    return new CreateConstructorMatchingSuperFix(aClass);
  }

  @Override
  public @NotNull IntentionAction createRemoveNewQualifierFix(@NotNull PsiNewExpression expression, PsiClass aClass) {
    return new RemoveNewQualifierFix(expression, aClass).asIntention();
  }

  @Override
  public @NotNull IntentionAction createSuperMethodReturnFix(@NotNull PsiMethod superMethod, @NotNull PsiType superMethodType) {
    return new MethodReturnTypeFix(superMethod, superMethodType, false, false, true);
  }

  @Override
  public @NotNull IntentionAction createInsertNewFix(@NotNull PsiMethodCallExpression call, @NotNull PsiClass aClass) {
    return new InsertNewFix(call, aClass).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddMethodBodyFix(@NotNull PsiMethod method) {
    return new AddMethodBodyFix(method).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddMethodBodyFix(@NotNull PsiMethod method, @NotNull @Nls String text) {
    return new AddMethodBodyFix(method, text).asIntention();
  }

  @Override
  public @NotNull IntentionAction createDeleteMethodBodyFix(@NotNull PsiMethod method) {
    return new DeleteMethodBodyFix(method).asIntention();
  }

  @Override
  public @NotNull IntentionAction createInsertSuperFix(@NotNull PsiMethod constructor) {
    return new InsertSuperFix(constructor).asIntention();
  }

  @Override
  public @NotNull IntentionAction createInsertThisFix(@NotNull PsiMethod constructor) {
    return new InsertThisFix(constructor).asIntention();
  }

  @Override
  public @NotNull IntentionAction createChangeMethodSignatureFromUsageFix(@NotNull PsiMethod targetMethod,
                                                                          PsiExpression @NotNull [] expressions,
                                                                          @NotNull PsiSubstitutor substitutor,
                                                                          @NotNull PsiElement context,
                                                                          boolean changeAllUsages,
                                                                          int minUsagesNumberToShowDialog) {
    return new ChangeMethodSignatureFromUsageFix(targetMethod, expressions, substitutor, context, changeAllUsages, minUsagesNumberToShowDialog);
  }

  @Override
  public @NotNull IntentionAction createChangeMethodSignatureFromUsageReverseOrderFix(@NotNull PsiMethod targetMethod,
                                                                                      PsiExpression @NotNull [] expressions,
                                                                                      @NotNull PsiSubstitutor substitutor,
                                                                                      @NotNull PsiElement context,
                                                                                      boolean changeAllUsages,
                                                                                      int minUsagesNumberToShowDialog) {
    return new ChangeMethodSignatureFromUsageReverseOrderFix(targetMethod,
                                                             expressions,
                                                             substitutor,
                                                             context,
                                                             changeAllUsages,
                                                             minUsagesNumberToShowDialog);
  }

  @Override
  public @NotNull List<IntentionAction> createCreateMethodFromUsageFixes(@NotNull PsiMethodCallExpression call) {
    return CreateMethodFromUsage.generateActions(call);
  }

  @Override
  public @NotNull IntentionAction createCreateMethodFromUsageFix(@NotNull PsiMethodReferenceExpression methodReferenceExpression) {
    return new CreateMethodFromMethodReferenceFix(methodReferenceExpression);
  }

  @Override
  public @NotNull List<IntentionAction> createCreateConstructorFromCallExpressionFixes(@NotNull PsiMethodCallExpression call) {
    return CreateConstructorFromUsage.generateConstructorActions(call);
  }

  @Override
  public @NotNull IntentionAction createReplaceWithTypePatternFix(@NotNull PsiReferenceExpression exprToReplace,
                                                                  @NotNull PsiClass resolvedExprClass,
                                                                  @NotNull String patternVarName) {
    return new ReplaceWithTypePatternFix(exprToReplace, resolvedExprClass, patternVarName).asIntention();
  }

  @Override
  public @NotNull IntentionAction createStaticImportMethodFix(@NotNull PsiMethodCallExpression call) {
    return new StaticImportMethodFix(call.getContainingFile(), call);
  }

  @Override
  public @NotNull IntentionAction createQualifyStaticMethodCallFix(@NotNull PsiMethodCallExpression call) {
    return new QualifyStaticMethodCallFix(call.getContainingFile(), call);
  }

  @Override
  public @NotNull IntentionAction createReplaceAddAllArrayToCollectionFix(@NotNull PsiMethodCallExpression call) {
    return new ReplaceAddAllArrayToCollectionFix(call).asIntention();
  }

  @Override
  public @NotNull List<IntentionAction> createCreateConstructorFromUsageFixes(@NotNull PsiConstructorCall call) {
    return CreateConstructorFromUsage.generateConstructorActions(call);
  }

  @Override
  public @NotNull List<IntentionAction> getVariableTypeFromCallFixes(@NotNull PsiMethodCallExpression call, @NotNull PsiExpressionList list) {
    return VariableTypeFromCallFix.getQuickFixActions(call, list);
  }

  @Override
  public @NotNull IntentionAction createAddReturnFix(@NotNull PsiParameterListOwner methodOrLambda) {
    return new AddReturnFix(methodOrLambda).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddVariableInitializerFix(@NotNull PsiVariable variable) {
    return new AddVariableInitializerFix(variable).asIntention();
  }

  @Override
  public @NotNull IntentionAction createDeferFinalAssignmentFix(@NotNull PsiVariable variable, @NotNull PsiReferenceExpression expression) {
    return new DeferFinalAssignmentFix(variable, expression).asIntention();
  }

  @Override
  public @NotNull IntentionAction createVariableAccessFromInnerClassFix(@NotNull PsiVariable variable, @NotNull PsiElement scope) {
    return new VariableAccessFromInnerClassFix(variable, scope);
  }

  @Override
  public @NotNull IntentionAction createCreateConstructorParameterFromFieldFix(@NotNull PsiField field) {
    return new CreateConstructorParameterFromFieldFix(field).asIntention();
  }

  @Override
  public @NotNull IntentionAction createInitializeFinalFieldInConstructorFix(@NotNull PsiField field) {
    return new InitializeFinalFieldInConstructorFix(field).asIntention();
  }

  @Override
  public @NotNull IntentionAction createChangeClassSignatureFromUsageFix(@NotNull PsiClass owner, @NotNull PsiReferenceParameterList parameterList) {
    return new ChangeClassSignatureFromUsageFix(owner, parameterList);
  }

  @Override
  public @NotNull IntentionAction createReplacePrimitiveWithBoxedTypeAction(@NotNull PsiTypeElement element,
                                                                            @NotNull String typeName,
                                                                            @NotNull String boxedTypeName) {
    return new ReplacePrimitiveWithBoxedTypeAction(element, typeName, boxedTypeName).asIntention();
  }

  @Override
  public @Nullable IntentionAction createReplacePrimitiveWithBoxedTypeAction(@NotNull PsiType operandType, @NotNull PsiTypeElement checkTypeElement) {
    PsiPrimitiveType primitiveType = ObjectUtils.tryCast(checkTypeElement.getType(), PsiPrimitiveType.class);
    if (primitiveType == null) return null;
    PsiClassType boxedType = primitiveType.getBoxedType(checkTypeElement);
    if (boxedType == null || !TypeConversionUtil.areTypesConvertible(operandType, boxedType)) return null;
    if (primitiveType.getBoxedTypeName() == null) return null;
    return createReplacePrimitiveWithBoxedTypeAction(checkTypeElement, primitiveType.getPresentableText(),
                                                     primitiveType.getBoxedTypeName());
  }

  @Override
  public @NotNull IntentionAction createMakeVarargParameterLastFix(@NotNull PsiVariable parameter) {
    return new MakeVarargParameterLastFix(parameter).asIntention();
  }

  @Override
  public @NotNull IntentionAction createMakeReceiverParameterFirstFix(@NotNull PsiReceiverParameter parameter) {
    return new MakeReceiverParameterFirstFix(parameter).asIntention();
  }

  @Override
  public @NotNull IntentionAction createMoveBoundClassToFrontFix(@NotNull PsiTypeParameter aClass, @NotNull PsiClassType type) {
    return new MoveBoundClassToFrontFix(aClass, type).asIntention();
  }

  @Override
  public void registerPullAsAbstractUpFixes(@NotNull PsiMethod method, @NotNull List<? super IntentionAction> registrar) {
    PullAsAbstractUpFix.registerQuickFix(method, registrar);
  }

  @Override
  public @NotNull IntentionAction createCreateAnnotationMethodFromUsageFix(@NotNull PsiNameValuePair pair) {
    return new CreateAnnotationMethodFromUsageFix(pair);
  }

  @Override
  @RequiresBackgroundThread
  public @NotNull ModCommandAction createOptimizeImportsFix(final boolean fixOnTheFly, @NotNull PsiFile file) {
    ApplicationManager.getApplication().assertIsNonDispatchThread();
    VirtualFile virtualFile = file.getVirtualFile();
    boolean isInContent = virtualFile != null && (ModuleUtilCore.projectContainsFile(file.getProject(), virtualFile, false) || ScratchUtil.isScratch(virtualFile));
    return new OptimizeImportsFix(fixOnTheFly, isInContent, virtualFile == null ? ThreeState.UNSURE : SilentChangeVetoer.extensionsAllowToChangeFileSilently(file.getProject(), virtualFile));
  }

  private static final class OptimizeImportsFix implements ModCommandAction {
    private final boolean myOnTheFly;
    private final boolean myInContent;
    private final ThreeState extensionsAllowToChangeFileSilently;

    private OptimizeImportsFix(boolean onTheFly, boolean isInContent, @NotNull ThreeState extensionsAllowToChangeFileSilently) {
      this.extensionsAllowToChangeFileSilently = extensionsAllowToChangeFileSilently;
      myOnTheFly = onTheFly;
      myInContent = isInContent;
    }

    @Override
    public @NotNull String getFamilyName() {
      return QuickFixBundle.message("optimize.imports.fix");
    }

    @Override
    public @Nullable Presentation getPresentation(@NotNull ActionContext context) {
      PsiFile file = context.file();
      if (!(file instanceof PsiJavaFile)) {
        return null;
      }
      if (ApplicationManager.getApplication().isDispatchThread() && myOnTheFly && !timeToOptimizeImports(file, myInContent, extensionsAllowToChangeFileSilently)) {
        return null;
      }
      VirtualFile virtualFile = file.getViewProvider().getVirtualFile();
      boolean available = myInContent ||
                  ScratchUtil.isScratch(virtualFile) ||
                  virtualFile.getFileSystem() instanceof NonPhysicalFileSystem;
      return available ? Presentation.of(getFamilyName()) : null;
    }

    @Override
    public @NotNull ModCommand perform(@NotNull ActionContext context) {
      PsiFile file = context.file();
      ModCommand command = ModCommand.psiUpdate(file, f -> {
        new JavaImportOptimizer().processFile(f).run();
      });
      if (command.isEmpty()) {
        VirtualFile vFile = file.getViewProvider().getVirtualFile();
        LOG.error("Import optimizer hasn't optimized any imports",
                  new Throwable(vFile.getPath()),
                  CoreAttachmentFactory.createAttachment(vFile));
      }
      return command;
    }
  }

  @Override
  public @NotNull IntentionAction createSafeDeleteUnusedParameterInHierarchyFix(@NotNull PsiParameter parameter,
                                                                                boolean excludingHierarchy) {
    if (excludingHierarchy) {
      InspectionToolWrapper<?, ?> toolWrapper = Objects.requireNonNull(InspectionProfileManager.getInstance(parameter.getProject())
                                                                         .getCurrentProfile().getInspectionTool("unused", parameter));
      InspectionProfileEntry tool = toolWrapper.getTool();
      return new UpdateInspectionOptionFix(tool, "members.myCheckParameterExcludingHierarchy",
        JavaErrorBundle.message("parameter.excluding.hierarchy.disable.text"), false).asIntention();
    }
    else {
      return new SafeDeleteFix(parameter);
    }
  }

  @Override
  public @NotNull IntentionAction createAddToDependencyInjectionAnnotationsFix(@NotNull Project project,
                                                                               @NotNull String qualifiedName) {
    return EntryPointsManagerBase.createAddEntryPointAnnotation(qualifiedName).asIntention();
  }

  @Override
  public @NotNull IntentionAction createAddToImplicitlyWrittenFieldsFix(@NotNull Project project, final @NotNull String qualifiedName) {
    return EntryPointsManagerBase.createAddImplicitWriteAnnotation(qualifiedName).asIntention();
  }

  @Override
  public @NotNull IntentionAction createCreateGetterOrSetterFix(boolean createGetter, boolean createSetter, @NotNull PsiField field) {
    if (createGetter && createSetter) {
      return new CreateGetterOrSetterFix.CreateGetterAndSetterFix(field).asIntention();
    }
    if (createGetter) {
