@Override
  protected void performRefactoring(final UsageInfo @NotNull [] usages) {
    final PsiManager manager = PsiManager.getInstance(myProject);
    final PsiElementFactory factory = JavaPsiFacade.getElementFactory(manager.getProject());

    final RefactoringElementListener elementListener = getTransaction().getElementListener(myInnerClass);
    try {
      PsiField field = null;
      if (myParameterNameOuterClass != null) {
        // pass outer as a parameter
        field = createOuterField(factory);
        field = addOuterField(field);
        myInnerClass = field.getContainingClass();
        addFieldInitializationToConstructors(Objects.requireNonNull(myInnerClass), field, myParameterNameOuterClass);
      }

      ChangeContextUtil.encodeContextInfo(myInnerClass, false);

      myInnerClass = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(myInnerClass);

      final MoveInnerOptions moveInnerOptions = new MoveInnerOptions(myInnerClass, myOuterClass, myTargetContainer, myNewClassName);
      final MoveInnerHandler handler = MoveInnerHandler.EP_NAME.forLanguage(myInnerClass.getLanguage());
      final PsiClass newClass;
      try {
        newClass = handler.copyClass(moveInnerOptions);
      }
      catch (IncorrectOperationException e) {
        RefactoringUIUtil.processIncorrectOperation(myProject, e);
        return;
      }

      // replace references in a new class to old inner class with references to itself
      for (PsiReference ref : ReferencesSearch.search(myInnerClass, new LocalSearchScope(newClass.getContainingFile()), true)) {
        PsiElement element = ref.getElement();
        if (element.getParent() instanceof PsiJavaCodeReferenceElement parentRef) {
          PsiElement parentRefElement = parentRef.resolve();
          if (parentRefElement instanceof PsiClass) { // reference to inner class inside our inner
            PsiImportStatementBase insertedImport = PsiTreeUtil.getParentOfType(parentRef, PsiImportStatementBase.class);
            if (insertedImport != null) {
              if (insertedImport instanceof PsiImportStaticStatement) {
                ref.bindToElement(newClass);
              }
              else {
                insertedImport.delete();
              }
              continue;
            }
            final PsiReferenceList referenceList = PsiTreeUtil.getTopmostParentOfType(parentRef, PsiReferenceList.class);
            if (referenceList == null || referenceList.getParent() != newClass) {
              parentRef.getQualifier().delete();
              continue;
            }
          }
        }
        ref.bindToElement(newClass);
      }

      List<PsiReference> referencesToRebind = new ArrayList<>();
      for (UsageInfo usage : usages) {
        if (usage.isNonCodeUsage) continue;
        PsiElement refElement = usage.getElement();
        if (refElement != null) {
          PsiReference[] references = refElement.getReferences();
          for (PsiReference reference : references) {
            if (reference.isReferenceTo(myInnerClass)) {
              referencesToRebind.add(reference);
            }
          }
        }
      }
      referencesToRebind.sort(Comparator.comparing(ref -> !(ref instanceof PsiImportStaticReferenceElement)));

      myInnerClass.delete();

      // correct references in usages
      for (UsageInfo usage : usages) {
        if (usage.isNonCodeUsage) continue; // should pass outer as parameter

        PsiElement element = usage.getElement();
        if (element == null) continue;

        MoveInnerClassUsagesHandler usagesHandler = MoveInnerClassUsagesHandler.EP_NAME.forLanguage(element.getLanguage());
        if (usagesHandler != null) {
          usagesHandler.correctInnerClassUsage(usage, myOuterClass, myParameterNameOuterClass);
        }
      }

      for (PsiReference reference : referencesToRebind) {
        if (!reference.getElement().isValid()) {
          //skip references which were changed during previous bindings
          continue;
        }
        reference.bindToElement(newClass);
      }

      for (UsageInfo usage : usages) {
        final PsiElement element = usage.getElement();
        final PsiElement parent = element != null ? element.getParent() : null;
        if (parent instanceof PsiNewExpression) {
          final PsiMethod resolveConstructor = ((PsiNewExpression)parent).resolveConstructor();
          for (PsiMethod method : newClass.getConstructors()) {
            if (resolveConstructor == method) {
              final PsiElement place = usage.getElement();
              if (place != null) {
                VisibilityUtil.escalateVisibility(method, place);
              }
              break;
            }
          }
        }
      }

      if (field != null) {
        final PsiExpression paramAccessExpression = factory.createExpressionFromText(myParameterNameOuterClass, null);
        for (final PsiMethod constructor : newClass.getConstructors()) {
          final PsiStatement[] statements = Objects.requireNonNull(constructor.getBody()).getStatements();
          if (statements.length > 0) {
            if (statements[0] instanceof PsiExpressionStatement) {
              PsiExpression expression = ((PsiExpressionStatement)statements[0]).getExpression();
              if (expression instanceof PsiMethodCallExpression) {
                @NonNls String text = ((PsiMethodCallExpression)expression).getMethodExpression().getText();
                if ("this".equals(text) || "super".equals(text)) {
                  ChangeContextUtil.decodeContextInfo(expression, myOuterClass, paramAccessExpression);
                }
              }
            }
          }
        }

        PsiExpression accessExpression = factory.createExpressionFromText(myFieldNameOuterClass, null);
        ChangeContextUtil.decodeContextInfo(newClass, myOuterClass, accessExpression);
      }
      else {
        ChangeContextUtil.decodeContextInfo(newClass, null, null);
      }

      if (myOpenInEditor) {
        EditorHelper.openInEditor(newClass);
      }

      if (myMoveCallback != null) {
        myMoveCallback.refactoringCompleted();
      }
      elementListener.elementMoved(newClass);

      List<NonCodeUsageInfo> nonCodeUsages = new ArrayList<>();
      for (UsageInfo usage : usages) {
        if (usage instanceof NonCodeUsageInfo) {
          nonCodeUsages.add((NonCodeUsageInfo)usage);
        }
      }
      myNonCodeUsages = nonCodeUsages.toArray(new NonCodeUsageInfo[0]);
    }
    catch (IncorrectOperationException e) {
      LOG.error(e);
    }
  }
@Override
  protected void performRefactoring(final UsageInfo @NotNull [] usages)