/**
   * Creates abstract version of each method in each class (does not touch method itself as opposite
   * to {@link #moveMethods(com.jetbrains.python.psi.PyClass, java.util.Collection,
   * com.jetbrains.python.psi.PyClass...)})
   *
   * @param currentFunctions functions to make them abstract
   * @param to classes where abstract method should be created
   */
  private static void makeMethodsAbstract(
      final Collection<PyFunction> currentFunctions, final PyClass... to) {
    final Set<PsiFile> filesToCheckImport = new HashSet<PsiFile>();
    final Set<PyClass> classesToAddMetaAbc = new HashSet<PyClass>();

    for (final PyFunction function : currentFunctions) {
      for (final PyClass destClass : to) {
        final PyFunctionBuilder functionBuilder =
            PyFunctionBuilder.copySignature(function, DECORATORS_MAY_BE_COPIED_TO_ABSTRACT);
        functionBuilder.decorate(PyNames.ABSTRACTMETHOD);
        final LanguageLevel level = LanguageLevel.forElement(destClass);
        PyClassRefactoringUtil.addMethods(
            destClass, false, functionBuilder.buildFunction(destClass.getProject(), level));
        classesToAddMetaAbc.add(destClass);
      }
    }

    // Add ABCMeta to new classes if needed
    for (final PyClass aClass : classesToAddMetaAbc) {
      if (addMetaAbcIfNeeded(aClass)) {
        filesToCheckImport.add(aClass.getContainingFile());
      }
    }

    // Add imports for ABC if needed
    for (final PsiFile file : filesToCheckImport) {
      addImportFromAbc(file, PyNames.ABSTRACTMETHOD);
      addImportFromAbc(file, ABC_META_CLASS);
      PyClassRefactoringUtil.optimizeImports(file); // To remove redundant imports
    }
  }
  /**
   * Moves methods (as opposite to {@link #makeMethodsAbstract(java.util.Collection,
   * com.jetbrains.python.psi.PyClass...)})
   *
   * @param from source
   * @param methodsToMove what to move
   * @param to where
   * @return newly added methods
   */
  private static List<PyElement> moveMethods(
      final PyClass from, final Collection<PyFunction> methodsToMove, final PyClass... to) {
    final List<PyElement> result = new ArrayList<PyElement>();
    for (final PyClass destClass : to) {
      // We move copies here because there may be several destinations
      final List<PyFunction> copies = new ArrayList<PyFunction>(methodsToMove.size());
      for (final PyFunction element : methodsToMove) {
        final PyFunction newMethod = (PyFunction) element.copy();
        copies.add(newMethod);
      }

      result.addAll(PyClassRefactoringUtil.copyMethods(copies, destClass));
    }
    deleteElements(methodsToMove);

    PyClassRefactoringUtil.insertPassIfNeeded(from);
    return result;
  }
 // TODO: Copy/Paste with PyClass.getMeta..
 private static boolean addMetaAbcIfNeeded(@NotNull final PyClass aClass) {
   final PsiFile file = aClass.getContainingFile();
   final PyType type = aClass.getMetaClassType(TypeEvalContext.userInitiated(file));
   if (type != null) {
     return false; // User already has metaclass. He probably knows about metaclasses, so we should
                   // not add ABCMeta
   }
   final LanguageLevel languageLevel = LanguageLevel.forElement(aClass);
   if (languageLevel
       .isPy3K()) { // TODO: Copy/paste, use strategy because we already has the same check in
                    // #couldBeAbstract
     // Add (metaclass= for Py3K
     PyClassRefactoringUtil.addSuperClassExpressions(
         aClass.getProject(),
         aClass,
         null,
         Collections.singletonList(Pair.create(PyNames.METACLASS, ABC_META_CLASS)));
   } else {
     // Add __metaclass__ for Py2
     PyClassRefactoringUtil.addClassAttributeIfNotExist(
         aClass, PyNames.DUNDER_METACLASS, ABC_META_CLASS);
   }
   return true;
 }