/** * 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; }