@NotNull
  public static Collection<PsiFile> doCopyClasses(final Map<PsiFile, PsiClass[]> fileToClasses,
                                                  @Nullable HashMap<PsiFile, String> fileToRelativePath, final String copyClassName,
                                                  final PsiDirectory targetDirectory,
                                                  final Project project) throws IncorrectOperationException {
    final Map<PsiClass, PsiClass> oldToNewMap = new HashMap<>();
    final List<PsiFile> createdFiles = new ArrayList<>(fileToClasses.size());
    final DumbService dumbService = DumbService.getInstance(project);
    final List<PsiFile> filesToProcess = checkExistingFiles(fileToClasses.keySet(), targetDirectory, fileToRelativePath, copyClassName);

    runWriteAction(project, RefactoringBundle.message("command.name.copy"), progress -> {
      for (int i = 0; i < filesToProcess.size(); i++) {
        final PsiFile psiFile = filesToProcess.get(i);
        if (progress != null) {
          progress.setIndeterminate(false);
          progress.setFraction((float)i/filesToProcess.size());
          progress.setText2(InspectionsBundle.message("processing.progress.text", psiFile.getName()));
        }
        final PsiClass[] sources = fileToClasses.get(psiFile);
        final String relativePath = fileToRelativePath != null ? fileToRelativePath.get(psiFile) : null;
        final PsiDirectoryImpl directory = WriteAction.compute(() ->
          (PsiDirectoryImpl) buildRelativeDir(targetDirectory, relativePath).findOrCreateTargetDirectory()
        );
        Ref<PsiFile> createdFileReference = new Ref<>();
        directory.executeWithUpdatingAddedFilesDisabled(() -> createdFileReference.set(copy(directory, psiFile, copyClassName)));
        final PsiFile createdFile = createdFileReference.get();
        if (createdFile == null) continue;
        createdFiles.add(createdFile);

        if (createdFile instanceof PsiClassOwner && sources != null) {
          Map<PsiClass, PsiClass> sourceToDestination = new LinkedHashMap<>();
          for (final PsiClass destination : ((PsiClassOwner)createdFile).getClasses()) {
            if (!isSynthetic(destination)) {
              PsiClass source = findByName(sources, destination.getName());
              if (source == null) {
                destination.delete();
              }
              else {
                sourceToDestination.put(source, destination);
              }
            }
          }

          for (final Map.Entry<PsiClass, PsiClass> classEntry : sourceToDestination.entrySet()) {
            if (copyClassName != null && sourceToDestination.size() == 1) {
              try {
                PsiClass newClass = dumbService.computeWithAlternativeResolveEnabled(() -> {
                  final PsiClass copy = copy(classEntry.getKey(), copyClassName);
                  return (PsiClass)classEntry.getValue().replace(copy);
                });
                oldToNewMap.put(classEntry.getKey(), newClass);
              }
              catch (IndexNotReadyException e) {
                //proceed with copy if references in one file were not restored
                LOG.error(e);
              }
            }
            else {
              oldToNewMap.put(classEntry.getKey(), classEntry.getValue());
            }
          }
        }
      }
    });

    dumbService.completeJustSubmittedTasks();
    CopyFilesOrDirectoriesHandler.updateAddedFiles(createdFiles, filesToProcess);

    runWriteAction(project, RefactoringBundle.message("copy.update.references"), progress -> {
      final Set<PsiElement> rebindExpressions = new HashSet<>();
      final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(project);
      float numberOfProcessedClasses = 0;
      for (PsiClass copiedClass : oldToNewMap.values()) {
        if (copiedClass == null) {
          LOG.error("Unexpected null value, keys = " + oldToNewMap.keySet());
          continue;
        }
        if (progress != null) {
          progress.setIndeterminate(false);
          progress.setFraction(numberOfProcessedClasses/filesToProcess.size());
          progress.setText2(InspectionsBundle.message("processing.progress.text", copiedClass.getName()));
          numberOfProcessedClasses++;
        }
        dumbService.runWithAlternativeResolveEnabled(() -> {
          decodeRefs(copiedClass, oldToNewMap, rebindExpressions);
          final PsiFile psiFile = copiedClass.getContainingFile();
          if (psiFile instanceof PsiJavaFile) {
            codeStyleManager.removeRedundantImports((PsiJavaFile)psiFile);
          }
          for (PsiElement expression : rebindExpressions) {
            //filter out invalid elements which are produced by nested elements:
            //new expressions/type elements, like: List<List<String>>; new Foo(new Foo()), etc
            if (expression.isValid()) {
              codeStyleManager.shortenClassReferences(expression);
            }
          }
        });
      }
    });

    new OptimizeImportsProcessor(project, createdFiles.toArray(PsiFile.EMPTY_ARRAY), null).run();
    return createdFiles;
  }
@NotNull
  public static Collection<PsiFile> doCopyClasses(final Map<PsiFile, PsiClass[]> fileToClasses,
                                                  @Nullable HashMap<PsiFile, String> fileToRelativePath, final String copyClassName,
                                                  final PsiDirectory targetDirectory,
                                                  final Project project)