diff --git a/platform/lang-api/src/com/intellij/refactoring/classMembers/MemberInfoBase.java b/platform/lang-api/src/com/intellij/refactoring/classMembers/MemberInfoBase.java index 924764ce3864..1ddbf5a65d83 100644 --- a/platform/lang-api/src/com/intellij/refactoring/classMembers/MemberInfoBase.java +++ b/platform/lang-api/src/com/intellij/refactoring/classMembers/MemberInfoBase.java @@ -66,7 +66,7 @@ public abstract class MemberInfoBase { } public T getMember() { - LOG.assertTrue(myMember.isValid()); + LOG.assertTrue(myMember.isValid(), myMember.getText() + " is invalid"); return myMember; } diff --git a/platform/lang-api/src/com/intellij/usageView/UsageViewDescriptor.java b/platform/lang-api/src/com/intellij/usageView/UsageViewDescriptor.java index 6800e0eb8562..0db0d6c3aff5 100644 --- a/platform/lang-api/src/com/intellij/usageView/UsageViewDescriptor.java +++ b/platform/lang-api/src/com/intellij/usageView/UsageViewDescriptor.java @@ -19,6 +19,9 @@ import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +/** + * TODO: Document + */ public interface UsageViewDescriptor { /** * @return an array of elements whose usages were searched or {@link com.intellij.psi.PsiElement#EMPTY_ARRAY} if not available diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index e8a92e6aee80..f572872ec2c7 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.psi; +import com.google.common.collect.Collections2; import com.google.common.collect.Maps; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.completion.PrioritizedLookupElement; @@ -52,6 +53,7 @@ import com.intellij.util.PlatformIcons; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; +import com.jetbrains.NotNullPredicate; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; @@ -63,6 +65,7 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.types.*; import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper; +import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -104,19 +107,9 @@ public class PyUtil { return node != null && node.getElementType().equals(TokenType.WHITE_SPACE); } - - @NotNull - public static Set getComments(PsiElement start) { - final Set comments = new HashSet(); - PsiElement seeker = start.getPrevSibling(); - if (seeker == null) seeker = start.getParent().getPrevSibling(); - while (seeker instanceof PsiWhiteSpace || seeker instanceof PsiComment) { - if (seeker instanceof PsiComment) { - comments.add(seeker); - } - seeker = seeker.getPrevSibling(); - } - return comments; + //TODO: Doctg + public static boolean isInit(@NotNull final PyFunction function) { + return PyNames.INIT.equals(function.getName()); } @Nullable @@ -1406,4 +1399,46 @@ public class PyUtil { } return false; } + + + private static boolean isObject(@NotNull final PyMemberInfo classMemberInfo) { + final PyElement element = classMemberInfo.getMember(); + if ((element instanceof PyClass) && PyNames.OBJECT.equals(element.getName())) { + return true; + } + return false; + + } + + /** + * Filters out {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} + * that should not be displayed in this refactoring (like object) + * + * @param pyMemberInfos collection to sort + * @return sorted collection + */ + @NotNull + public static Collection filterOutObject(@NotNull final Collection pyMemberInfos) { + return Collections2.filter(pyMemberInfos, new ObjectPredicate(false)); + } + + + /** + * Filters only pyclass object (new class) + */ + public static class ObjectPredicate extends NotNullPredicate { + private final boolean myAllowObjects; + + /** + * @param allowObjects allows only objects if true. Allows all but objects otherwise. + */ + public ObjectPredicate(final boolean allowObjects) { + myAllowObjects = allowObjects; + } + + @Override + public boolean applyNotNull(@NotNull final PyMemberInfo input) { + return myAllowObjects == isObject(input); + } + } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java index 82b21adb0a0b..bd8dc2aeaf5f 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java @@ -25,6 +25,8 @@ import com.jetbrains.python.PythonFileType; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.psi.PyElementGenerator; import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyUtil; +import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import java.util.ArrayList; import java.util.Collections; diff --git a/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java index 1ebe50f95078..875a07f58ab6 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java +++ b/python/src/com/jetbrains/python/refactoring/classes/PyClassRefactoringUtil.java @@ -15,19 +15,20 @@ */ package com.jetbrains.python.refactoring.classes; +import com.google.common.base.Function; +import com.google.common.collect.Collections2; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilBase; -import com.intellij.psi.util.PsiUtilCore; import com.intellij.psi.util.QualifiedName; +import com.jetbrains.NotNullPredicate; import com.jetbrains.python.PyNames; -import com.jetbrains.python.PythonFileType; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.codeInsight.imports.AddImportHelper; import com.jetbrains.python.psi.*; @@ -53,160 +54,99 @@ public class PyClassRefactoringUtil { private static final Key ENCODED_USE_FROM_IMPORT = Key.create("PyEncodedUseFromImport"); private static final Key ENCODED_IMPORT_AS = Key.create("PyEncodedImportAs"); + private static final InitFirst INIT_FIRST = new InitFirst(); + private PyClassRefactoringUtil() { } - public static void moveSuperclasses(PyClass clazz, Set superClasses, PyClass superClass) { - if (superClasses.size() == 0) return; - final Project project = clazz.getProject(); - final List toAdd = removeAndGetSuperClasses(clazz, superClasses); - addSuperclasses(project, superClass, toAdd, superClasses); - } - - public static void addSuperclasses(Project project, - PyClass superClass, - @Nullable Collection superClassesAsPsi, - Collection superClassesAsStrings) { - if (superClassesAsStrings.size() == 0) return; - PyArgumentList argList = superClass.getSuperClassExpressionList(); - if (argList != null) { - if (superClassesAsPsi != null) { - for (PyExpression element : superClassesAsPsi) { - argList.addArgument(element); - } - } - else { - for (String s : superClassesAsStrings) { - argList.addArgument(PyElementGenerator.getInstance(project).createExpressionFromText(s)); - } - } - } - else { - addSuperclasses(project, superClass, superClassesAsStrings); - } - } /** - * Removes super classes by name and returns list of removed + * TODO: Doc + * Copies class field declarations to some other place * - * @param clazz class to find super classes to remove - * @param superClassesToRemove list of super class names - * @return list of removed classes + * @param assignmentStatement list of class fields */ @NotNull - public static List removeAndGetSuperClasses(@NotNull PyClass clazz, @NotNull Set superClassesToRemove) { - if (superClassesToRemove.isEmpty()) { - return Collections.emptyList(); - } - final List result = new ArrayList(); - for (final PyExpression superClassExpression : clazz.getSuperClassExpressions()) { - if (superClassesToRemove.contains(superClassExpression.getText())) { - result.add(superClassExpression); - superClassExpression.delete(); - } - } - return result; - } - - public static void addSuperclasses(Project project, PyClass superClass, Collection superClasses) { - if (superClasses.size() == 0) return; - final StringBuilder builder = new StringBuilder("("); - boolean hasChanges = false; - for (String element : superClasses) { - if (builder.length() > 1) builder.append(","); - if (!alreadyHasSuperClass(superClass, element)) { - builder.append(element); - hasChanges = true; - } - } - builder.append(")"); - if (!hasChanges) return; - - final PsiFile file = - PsiFileFactory.getInstance(project).createFileFromText(superClass.getName() + "temp", PythonFileType.INSTANCE, builder.toString()); - final PsiElement expression = file.getFirstChild().getFirstChild(); - PsiElement colon = superClass.getFirstChild(); - while (colon != null && !colon.getText().equals(":")) { - colon = colon.getNextSibling(); - } - LOG.assertTrue(colon != null && expression != null); - PyPsiUtils.addBeforeInParent(colon, expression); - } - - private static boolean alreadyHasSuperClass(PyClass superClass, String className) { - for (PyClass aClass : superClass.getSuperClasses()) { - if (Comparing.strEqual(aClass.getName(), className)) { - return true; - } - } - return false; - } - - /** - * Moves class field declarations to some other place - * @param expressions list of class fields - * @param superClass where to move them - */ - public static void moveFieldDeclarationToStatement(@NotNull final Collection expressions, - @NotNull final PyStatementList superClassStatement) { - for (final PyTargetExpression expression : expressions) { - final PyAssignmentStatement expAssignmentStatement = PsiTreeUtil.getParentOfType(expression, PyAssignmentStatement.class); - assert expAssignmentStatement != null: "Target expression has no assignment statement"; - PyUtil.addElementToStatementList(expAssignmentStatement.copy(), superClassStatement, true); - expAssignmentStatement.delete(); + public static List copyFieldDeclarationToStatement(@NotNull final Collection assignmentStatement, + @NotNull final PyStatementList superClassStatement) { + List declations = new ArrayList(assignmentStatement.size()); + for (final PyAssignmentStatement expression : assignmentStatement) { + PyAssignmentStatement newDeclaration = (PyAssignmentStatement)expression.copy(); + declations.add(newDeclaration); + declations.add((PyAssignmentStatement)PyUtil.addElementToStatementList(newDeclaration, superClassStatement, true)); PyPsiUtils.removeRedundantPass(superClassStatement); } - + return declations; } - public static void moveMethods(Collection methods, PyClass superClass) { - if (methods.size() == 0) return; + + public static List copyMethods(Collection methods, PyClass superClass) { + if (methods.size() == 0) return null; for (PsiElement e : methods) { rememberNamedReferences(e); } - final PyElement[] elements = methods.toArray(new PyElement[methods.size()]); - addMethods(superClass, elements, true); - removeMethodsWithComments(elements); + final PyFunction[] elements = methods.toArray(new PyFunction[methods.size()]); + return addMethods(superClass, elements); } - private static void removeMethodsWithComments(PyElement[] elements) { - for (PyElement element : elements) { - final Set comments = PyUtil.getComments(element); - if (comments.size() > 0) { - PyPsiUtils.removeElements(PsiUtilCore.toPsiElementArray(comments)); + //TODO: Doct + @NotNull + public static List addMethods(@NotNull PyClass destination, @NotNull PyFunction... methods) { + List methodsToAdd = new ArrayList(Arrays.asList(methods)); + Collections.sort(methodsToAdd, INIT_FIRST); + + PyStatementList destStatementList = destination.getStatementList(); + List newlyCreatedMethods = new ArrayList(methods.length); + + for (PyFunction method : methodsToAdd) { + + if (destination.findMethodByName(method.getName(), false) != null) { + continue; //TODO: Doc why + } + + + PyFunction newMethod = insertMethodInProperPlace(destStatementList, method); + newlyCreatedMethods.add(newMethod); + restoreNamedReferences(newMethod); + } + + PyPsiUtils.removeRedundantPass(destStatementList); + return newlyCreatedMethods; + } + + //TODO: Doc algo + @NotNull + private static PyFunction insertMethodInProperPlace( + @NotNull PyStatementList destStatementList, + @NotNull PyFunction method) { + boolean methodIsInit = PyUtil.isInit(method); + if (!methodIsInit) { + //Not init method could be inserted in the bottom + return (PyFunction)destStatementList.add(method); + } + + //We should find appropriate place for init + for (PsiElement element : destStatementList.getChildren()) { + boolean elementComment = element instanceof PyExpressionStatement; + boolean elementClassField = element instanceof PyAssignmentStatement; + + if ((!(elementComment || elementClassField))) { + return (PyFunction)destStatementList.addBefore(method, element); } } - PyPsiUtils.removeElements(elements); + return (PyFunction)destStatementList.add(method); } - public static void insertPassIfNeeded(@NotNull T element) { + + public static void insertPassIfNeeded(@NotNull T element) { final PyStatementList statements = element.getStatementList(); if (statements.getStatements().length == 0) { statements.add( - PyElementGenerator.getInstance(element.getProject()).createFromText(LanguageLevel.getDefault(), PyPassStatement.class, PyNames.PASS)); + PyElementGenerator.getInstance(element.getProject()) + .createFromText(LanguageLevel.getDefault(), PyPassStatement.class, PyNames.PASS) + ); } } - public static void addMethods(final PyClass superClass, final PyElement[] elements, final boolean up) { - if (elements.length == 0) return; - final PyStatementList statements = superClass.getStatementList(); - for (PyElement newStatement : elements) { - if (up && newStatement instanceof PyFunction) { - final String name = newStatement.getName(); - if (name != null && superClass.findMethodByName(name, false) != null) { - continue; - } - } - if (newStatement instanceof PyExpressionStatement && newStatement.getFirstChild() instanceof PyStringLiteralExpression) continue; - final PsiElement anchor = statements.add(newStatement); - restoreNamedReferences(anchor); - final Set comments = PyUtil.getComments(newStatement); - for (PsiElement comment : comments) { - statements.addBefore(comment, anchor); - } - } - PyPsiUtils.removeRedundantPass(statements); - } - public static void restoreNamedReferences(@NotNull PsiElement element) { restoreNamedReferences(element, null); } @@ -231,6 +171,7 @@ public class PyClassRefactoringUtil { }); } + private static void restoreReference(final PyReferenceExpression node) { PsiNamedElement target = node.getCopyableUserData(ENCODED_IMPORT); final String asName = node.getCopyableUserData(ENCODED_IMPORT_AS); @@ -443,9 +384,10 @@ public class PyClassRefactoringUtil { /** * Creates class method - * @param methodName name if new method (be sure to check {@link com.jetbrains.python.PyNames} for special methods) - * @param pyClass class to add method - * @param modifier if method static or class or simple instance method (null)> + * + * @param methodName name if new method (be sure to check {@link com.jetbrains.python.PyNames} for special methods) + * @param pyClass class to add method + * @param modifier if method static or class or simple instance method (null)> * @param parameterNames method parameters * @return newly created method */ @@ -473,8 +415,55 @@ public class PyClassRefactoringUtil { builder.parameter(parameterName); } - final PyFunction function = builder.addFunction(pyClass.getStatementList(), LanguageLevel.getDefault()); - addMethods(pyClass, new PyElement[]{function}, true); - return function; + final PyFunction function = builder.buildFunction(pyClass.getProject(), LanguageLevel.getDefault()); + return addMethods(pyClass, function).get(0); + } + + //TODO: Doc + @NotNull + public static void addSuperclasses(@NotNull final Project project, + @NotNull final PyClass clazz, + @NotNull final PyClass... superClasses) { + + final List superClassNames = new ArrayList(); + + + for (final PyClass superClass : Collections2.filter(Arrays.asList(superClasses), NotNullPredicate.INSTANCE)) { + if (superClass.getName() != null) { + superClassNames.add(superClass.getName()); + PyClassRefactoringUtil.insertImport(clazz, superClass); + } + } + + PyArgumentList superClassExpressionList = clazz.getSuperClassExpressionList(); + PyElementGenerator generator = PyElementGenerator.getInstance(project); + + if (superClassExpressionList != null) { + for (String superClassName : superClassNames) { + superClassExpressionList.addArgument(generator.createExpressionFromText(superClassName)); + } + } + //TODO: Doc why we do it manually + else { + String superClassText = String.format("(%s)", StringUtil.join(superClassNames, ",")); + clazz.addAfter(generator.createExpressionFromText(superClassText), + clazz.getNameNode().getPsi()); + } + } + + private static class NameExtractor implements Function { + @SuppressWarnings("NullableProblems") //We sure collection has no null + @Nullable + @Override + public String apply(@NotNull final PyElement input) { + return input.getName(); + } + } + + private static class InitFirst implements Comparator { + @Override + public int compare(PyFunction o1, PyFunction o2) { + return (PyUtil.isInit(o1) ? 1 : 0) - (PyUtil.isInit(o2) ? 1 : 0); + } } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java index 67a501368c1b..747d9bf560b0 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java +++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHandler.java @@ -21,6 +21,7 @@ import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler; import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage; import com.jetbrains.python.vp.Creator; @@ -40,7 +41,7 @@ public class PyExtractSuperclassHandler extends PyClassRefactoringHandler { @NotNull final PyMemberInfoStorage infoStorage, @NotNull final Editor editor) { //TODO: Move to presenter - if (PyExtractSuperclassHelper.filterOutDeniedMembers(infoStorage.getClassMemberInfos(classUnderRefactoring)).isEmpty()) { + if (PyUtil.filterOutObject(infoStorage.getClassMemberInfos(classUnderRefactoring)).isEmpty()) { CommonRefactoringUtil.showErrorHint(project, editor, PyBundle .message("refactoring.extract.super.class.no.members.allowed"), RefactoringBundle.message("extract.superclass.elements.header"), null); diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java index ce3c93471751..915d53ab5410 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java +++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassHelper.java @@ -33,10 +33,7 @@ import com.intellij.util.PathUtil; import com.jetbrains.NotNullPredicate; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonFileType; -import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyElement; -import com.jetbrains.python.psi.PyElementGenerator; +import com.jetbrains.python.psi.*; import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import com.jetbrains.python.refactoring.classes.membersManager.MembersManager; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; @@ -55,7 +52,7 @@ public final class PyExtractSuperclassHelper { /** * Accepts only those members whose element is PyClass object (new classes) */ - private static final Predicate ALLOW_OBJECT = new ObjectPredicate(true); + private static final Predicate ALLOW_OBJECT = new PyUtil.ObjectPredicate(true); private PyExtractSuperclassHelper() { } @@ -81,8 +78,8 @@ public final class PyExtractSuperclassHelper { newClass = placeNewClass(project, newClass, clazz, targetFile); - MembersManager.moveAllMembers(clazz, newClass, selectedMemberInfos); - PyClassRefactoringUtil.addSuperclasses(project, clazz, null, Collections.singleton(superBaseName)); + MembersManager.moveAllMembers(selectedMemberInfos, clazz, newClass); + PyClassRefactoringUtil.addSuperclasses(project, clazz, null, newClass); } @@ -226,45 +223,4 @@ public final class PyExtractSuperclassHelper { } return ret; } - - - private static boolean isObject(@NotNull final PyMemberInfo classMemberInfo) { - final PyElement element = classMemberInfo.getMember(); - if ((element instanceof PyClass) && PyNames.OBJECT.equals(element.getName())) { - return true; - } - return false; - - } - - /** - * Filters out {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} - * that should not be displayed in this refactoring (like object) - * - * @param pyMemberInfos collection to sort - * @return sorted collection - */ - @NotNull - static Collection filterOutDeniedMembers(@NotNull final Collection pyMemberInfos) { - return Collections2.filter(pyMemberInfos, new ObjectPredicate(false)); - } - - /** - * Filters only pyclass object (new class) - */ - private static class ObjectPredicate extends NotNullPredicate { - private final boolean myAllowObjects; - - /** - * @param allowObjects allows only objects if true. Allows all but objects otherwise. - */ - private ObjectPredicate(final boolean allowObjects) { - myAllowObjects = allowObjects; - } - - @Override - public boolean applyNotNull(@NotNull final PyMemberInfo input) { - return myAllowObjects == isObject(input); - } - } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java index 0bf5436180c5..dcc9a96a831f 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java +++ b/python/src/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassPresenterImpl.java @@ -13,6 +13,7 @@ import com.intellij.util.containers.MultiMap; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; import com.jetbrains.python.refactoring.classes.membersManager.vp.BadDataException; @@ -75,7 +76,7 @@ class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImp final String defaultFilePath = FileUtil.toSystemDependentName(myClassUnderRefactoring.getContainingFile().getVirtualFile().getPath()); final VirtualFile[] roots = ProjectRootManager.getInstance(myClassUnderRefactoring.getProject()).getContentRoots(); final Collection pyMemberInfos = - PyExtractSuperclassHelper.filterOutDeniedMembers(myStorage.getClassMemberInfos(myClassUnderRefactoring)); + PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring)); myView.configure( new PyExtractSuperclassInitializationInfo(new PyExtractSuperclassInfoModel(myClassUnderRefactoring), pyMemberInfos, defaultFilePath, roots)); diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java index 239f682be4e6..fefa4cfc9e3a 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/ClassFieldsManager.java @@ -1,5 +1,6 @@ package com.jetbrains.python.refactoring.classes.membersManager; +import com.jetbrains.python.psi.PyAssignmentStatement; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyTargetExpression; import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; @@ -21,10 +22,15 @@ class ClassFieldsManager extends FieldsManager { @Override - protected void moveMembers(@NotNull final PyClass from, - @NotNull final PyClass to, - @NotNull final Collection members) { - PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, to.getStatementList()); + protected void moveAssignments(@NotNull PyClass from, + @NotNull Collection statements, + @NotNull PyClass... to) { + //TODO: Copy/paste with InstanceFieldsManager. Move to parent? + for (PyClass destClass : to) { + PyClassRefactoringUtil.copyFieldDeclarationToStatement(statements, destClass.getStatementList()); + } + deleteElements(statements); + PyClassRefactoringUtil.insertPassIfNeeded(from); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java index d97af6d0c703..ca8729cb7a1c 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/FieldsManager.java @@ -1,8 +1,10 @@ package com.jetbrains.python.refactoring.classes.membersManager; +import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.NotNullPredicate; import com.jetbrains.python.psi.PyAssignmentStatement; import com.jetbrains.python.psi.PyClass; @@ -11,6 +13,7 @@ import com.jetbrains.python.psi.PyTargetExpression; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.List; /** @@ -36,6 +39,18 @@ abstract class FieldsManager extends MembersManager { return Lists.newArrayList(Collections2.filter(getFieldsByClass(pyClass), SIMPLE_ASSIGNMENTS_ONLY)); } + @NotNull + @Override + protected void moveMembers(@NotNull PyClass from, + @NotNull Collection members, + @NotNull PyClass... to) { + moveAssignments(from, Collections2.transform(members, new AssignmentTransform()), to); + } + + protected abstract void moveAssignments(@NotNull PyClass from, + @NotNull Collection statements, + @NotNull PyClass... to); + /** * Checks if class has fields. Only child may know how to obtain field * @@ -84,4 +99,12 @@ abstract class FieldsManager extends MembersManager { return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass()); } } + + + private class AssignmentTransform implements Function { + @Override + public PyAssignmentStatement apply(PyTargetExpression input) { + return PsiTreeUtil.getParentOfType(input, PyAssignmentStatement.class); + } + } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java index 7ed18fb3ecf2..53dfcc7cd236 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/InstanceFieldsManager.java @@ -5,7 +5,9 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; /** @@ -18,22 +20,35 @@ class InstanceFieldsManager extends FieldsManager { @Override - protected void moveMembers(@NotNull final PyClass from, - @NotNull final PyClass to, - @NotNull final Collection members) { + protected void moveAssignments(@NotNull PyClass from, + @NotNull Collection statements, + @NotNull PyClass... to) { + //TODO: Copy/paste with ClassFieldsManager. Move to parent? + + for (PyClass destClass : to) { + copyInstanceFields(statements, destClass); + } + + deleteElements(statements); + + final PyFunction fromInitMethod = from.findMethodByName(PyNames.INIT, false); + if (fromInitMethod != null) { + //We can't leave class constructor with empty body + PyClassRefactoringUtil.insertPassIfNeeded(fromInitMethod); + } + } + + //TODO: Doc + @NotNull + private static List copyInstanceFields(@NotNull final Collection members, + @NotNull final PyClass to) { //We need __init__ method, and if there is no any -- we need to create it PyFunction toInitMethod = to.findMethodByName(PyNames.INIT, false); if (toInitMethod == null) { toInitMethod = PyClassRefactoringUtil.createMethod(PyNames.INIT, to, null); } final PyStatementList statementList = toInitMethod.getStatementList(); - PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, statementList); - - final PyFunction fromInitMethod = from.findMethodByName(PyNames.INIT, false); - if (fromInitMethod !=null) { - //We can't leave class constructor with empty body - PyClassRefactoringUtil.insertPassIfNeeded(fromInitMethod); - } + return PyClassRefactoringUtil.copyFieldDeclarationToStatement(members, statementList); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java index c6b313291a90..2fd737fb7b21 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MembersManager.java @@ -6,23 +6,23 @@ import com.google.common.base.Predicate; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Collections2; import com.google.common.collect.Multimap; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.jetbrains.NotNullPredicate; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; +import java.util.*; /** + * TODO: Extract "from, to[], members, deleteFromOrigin" to separate class? * Moves members between classes via its plugins (managers). - * To move members use {@link #getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)} and {@link #moveMembers(com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass, java.util.Collection)} + * To move members use {@link #getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)} and {@link #moveMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass)} * To add new manager, extend this class and add it to {@link #MANAGERS} * * @author Ilya.Kazakevich @@ -68,15 +68,18 @@ public abstract class MembersManager implements Function memberInfos) { + public static void moveAllMembers( + @NotNull final Collection memberInfos, + @NotNull final PyClass from, + @NotNull final PyClass... to + ) { final Multimap, PyMemberInfo> managerToMember = ArrayListMultimap.create(); //Collect map (manager)->(list_of_memebers) for (final PyMemberInfo memberInfo : memberInfos) { @@ -84,20 +87,23 @@ public abstract class MembersManager implements Function membersManager : managerToMember.keySet()) { - moveSafely(from, to, membersManager, Collections2.transform(managerToMember.get(membersManager), PY_MEMBER_EXTRACTOR)); + Collection elementsToMove = Collections2.transform(managerToMember.get(membersManager), PY_MEMBER_EXTRACTOR); + moveSafely(from, membersManager, elementsToMove, to); } PyClassRefactoringUtil.insertPassIfNeeded(from); } @SuppressWarnings({"unchecked", "rawtypes"}) //We check classes at runtime - private static void moveSafely(@NotNull final PyClass from, - @NotNull final PyClass to, - @NotNull final MembersManager manager, - @NotNull final Collection elementsToMove) { + private static void moveSafely( + @NotNull final PyClass from, + @NotNull final MembersManager manager, + @NotNull final Collection elementsToMove, + @NotNull final PyClass... to) { manager.checkElementTypes(elementsToMove); - manager.moveMembers(from, to, (Collection)elementsToMove); + manager.moveMembers(from, (Collection)elementsToMove, to); } + /** * Checks that all elements has allowed type for manager * @@ -171,13 +177,17 @@ public abstract class MembersManager implements Function + * TODO: Doc + * TODO: Doc who and what should return (write about elemnents) */ - protected abstract void moveMembers(@NotNull PyClass from, @NotNull PyClass to, @NotNull Collection members); + @NotNull + protected abstract void moveMembers( + @NotNull PyClass from, + @NotNull Collection members, + @NotNull PyClass... to); + /** * Creates {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} from {@link com.jetbrains.python.psi.PyElement} @@ -191,6 +201,13 @@ public abstract class MembersManager implements Function pyElementsToDelete) { + for (PsiElement element : pyElementsToDelete) { + element.delete(); + } + } + private static class PyMemberExtractor implements Function { @SuppressWarnings("NullableProblems") //IDEA-120100 @Override diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java index af33f861814e..d9643e976911 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/MethodsManager.java @@ -6,6 +6,7 @@ import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -28,8 +29,22 @@ class MethodsManager extends MembersManager { } @Override - protected void moveMembers(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection members) { - PyClassRefactoringUtil.moveMethods(members, to); + protected void moveMembers(@NotNull PyClass from, + @NotNull Collection members, + @NotNull PyClass... to) { + for (PyClass destClass : to) { + //We move copies here becase we there may be several destinations + List copies = new ArrayList(members.size()); + for (PyFunction member : members) { + PyFunction newMethod = (PyFunction)member.copy(); + copies.add(newMethod); + } + + PyClassRefactoringUtil.copyMethods(copies, destClass); + } + deleteElements(members); + + PyClassRefactoringUtil.insertPassIfNeeded(from); } @NotNull diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java index 295a5a6253fa..e923ea8b611b 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyMembersRefactoringBaseProcessor.java @@ -1,5 +1,6 @@ package com.jetbrains.python.refactoring.classes.membersManager; +import com.intellij.psi.PsiElement; import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; @@ -11,6 +12,8 @@ import java.util.Collection; import java.util.List; /** + * TODO: Doc (and why it extends 2 classes) + * * @author Ilya.Kazakevich */ public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringProcessor implements UsageViewDescriptor { @@ -18,17 +21,19 @@ public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringP @NotNull protected final Collection myMembersToMove; @NotNull - protected final PyClass myTo; - @NotNull protected final PyClass myFrom; + @NotNull + private final PyClass[] myTo; - protected PyMembersRefactoringBaseProcessor(@NotNull final PyClass from, - @NotNull final PyClass to, - @NotNull final Collection membersToMove) { + //TODO: Doc + protected PyMembersRefactoringBaseProcessor( + @NotNull final Collection membersToMove, + @NotNull final PyClass from, + @NotNull final PyClass... to) { super(from.getProject()); myFrom = from; - myTo = to; myMembersToMove = new ArrayList(membersToMove); + myTo = to.clone(); } @NotNull @@ -39,24 +44,32 @@ public abstract class PyMembersRefactoringBaseProcessor extends BaseRefactoringP @NotNull @Override - protected UsageInfo[] findUsages() { - final List result = new ArrayList(myMembersToMove.size()); - for (final PyMemberInfo pyMemberInfo : myMembersToMove) { - result.add(new PyUsageInfo(pyMemberInfo)); - } - return result.toArray(new UsageInfo[result.size()]); + public PsiElement[] getElements() { + return myTo.clone(); } + //TODO: Doc + @NotNull @Override - protected void performRefactoring(final UsageInfo[] usages) { - final Collection membersToMoveFromUsage = new ArrayList(usages.length); + protected final PyUsageInfo[] findUsages() { + final List result = new ArrayList(myTo.length); + for (final PyClass pyDestinationClass : myTo) { + result.add(new PyUsageInfo(pyDestinationClass)); + } + return result.toArray(new PyUsageInfo[result.size()]); + } + + //TODO: Doc + @Override + protected final void performRefactoring(final UsageInfo[] usages) { + final Collection destinations = new ArrayList(usages.length); for (final UsageInfo usage : usages) { if (!(usage instanceof PyUsageInfo)) { throw new IllegalArgumentException("Only PyUsageInfo is accepted here"); } //TODO: Doc - membersToMoveFromUsage.add(((PyUsageInfo)usage).getPyMemberInfo()); + destinations.add(((PyUsageInfo)usage).getTo()); } - MembersManager.moveAllMembers(myFrom, myTo, membersToMoveFromUsage); + MembersManager.moveAllMembers(myMembersToMove, myFrom, destinations.toArray(new PyClass[destinations.size()])); } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java index dc546d9423df..81d811316818 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/PyUsageInfo.java @@ -1,24 +1,26 @@ package com.jetbrains.python.refactoring.classes.membersManager; import com.intellij.usageView.UsageInfo; +import com.jetbrains.python.psi.PyClass; import org.jetbrains.annotations.NotNull; /** * TODO: Make it generic to allow to reuse in another projects? + * TODO: Document * * @author Ilya.Kazakevich */ class PyUsageInfo extends UsageInfo { @NotNull - private final PyMemberInfo myPyMemberInfo; + private final PyClass myTo; - PyUsageInfo(@NotNull final PyMemberInfo pyMemberInfo) { - super(pyMemberInfo.getMember(), true); - myPyMemberInfo = pyMemberInfo; + PyUsageInfo(@NotNull PyClass to) { + super(to, true); //TODO: Make super generic and get rid of field? + this.myTo = to; } @NotNull - PyMemberInfo getPyMemberInfo() { - return myPyMemberInfo; + public PyClass getTo() { + return myTo; } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java index ae37182d6d0e..0b238a552239 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/SuperClassesManager.java @@ -1,19 +1,17 @@ package com.jetbrains.python.refactoring.classes.membersManager; -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.common.collect.Sets; -import com.intellij.psi.PsiNamedElement; import com.intellij.refactoring.RefactoringBundle; -import com.jetbrains.NotNullPredicate; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * Plugin that moves superclasses from one class to another @@ -25,7 +23,6 @@ class SuperClassesManager extends MembersManager { super(PyClass.class); } - private static final NameExtractor NAME_EXTRACTOR = new NameExtractor(); @NotNull @Override @@ -34,16 +31,18 @@ class SuperClassesManager extends MembersManager { } @Override - protected void moveMembers(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection members) { - final Set superClassesToMove = - Sets.newHashSet(Collections2.filter(Collections2.transform(members, NAME_EXTRACTOR), NotNullPredicate.INSTANCE)); - - for (final PyElement member : members) { - superClassesToMove.add(member.getName()); + protected void moveMembers(@NotNull PyClass from, @NotNull Collection members, @NotNull PyClass... to) { + for (PyClass destClass : to) { + PyClassRefactoringUtil.addSuperclasses(from.getProject(), destClass, members.toArray(new PyClass[members.size()])); } - PyClassRefactoringUtil.moveSuperclasses(from, superClassesToMove, to); - PyClassRefactoringUtil.insertImport(to, new ArrayList(members)); + for (PyExpression expression : from.getSuperClassExpressions()) { + for (PyClass member : members) { + if (expression.getText().equals(member.getName())) { + expression.delete(); + } + } + } } @NotNull @@ -53,13 +52,4 @@ class SuperClassesManager extends MembersManager { //TODO: Check for "overrides" return new PyMemberInfo(input, false, name, false, this); } - - private static class NameExtractor implements Function { - @SuppressWarnings("NullableProblems") //We sure collection has no null - @Nullable - @Override - public String apply(@NotNull final PyElement input) { - return input.getName(); - } - } } diff --git a/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java b/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java index b730fd4e3c06..9fb380a4effe 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java +++ b/python/src/com/jetbrains/python/refactoring/classes/membersManager/package-info.java @@ -1,7 +1,7 @@ /** * Incapsulates knowledge about class members that could be moved to some other class. * To use (get list of members to move or actually move them) use {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#getAllMembersCouldBeMoved(com.jetbrains.python.psi.PyClass)} - * and {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#moveAllMembers(com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass, java.util.Collection)} + * and {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager#moveAllMembers(java.util.Collection, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyClass)} * * This class delegates its behaviour to its managers (some kind of plugins). There is one for each member type (one for method, one for field etc). * You need to extend {@link com.jetbrains.python.refactoring.classes.membersManager.MembersManager} to add some. See its javadoc for more info. diff --git a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java index 93107bb8f1e2..a660056a9092 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpProcessor.java @@ -1,6 +1,5 @@ package com.jetbrains.python.refactoring.classes.pullUp; -import com.intellij.psi.PsiElement; import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; @@ -11,18 +10,14 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; /** + * TODO: Doc + * * @author Ilya.Kazakevich */ class PyPullUpProcessor extends PyMembersRefactoringBaseProcessor { PyPullUpProcessor(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection membersToMove) { - super(from, to, membersToMove); - } - - @NotNull - @Override - public PsiElement[] getElements() { - return new PsiElement[]{myTo}; + super(membersToMove, from, to); } diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java index 7e4f93c5493e..8f64adc86ded 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownConflicts.java @@ -34,7 +34,7 @@ import java.util.List; /** * @author Dennis.Ushakov */ -public class PyPushDownConflicts { +class PyPushDownConflicts { private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName()); private final PyClass myClass; @@ -47,11 +47,11 @@ public class PyPushDownConflicts { myConflicts = new MultiMap(); } - public MultiMap getConflicts() { + MultiMap getConflicts() { return myConflicts; } - public void checkTargetClassConflicts(PyClass clazz) { + void checkTargetClassConflicts(PyClass clazz) { checkPlacementConflicts(clazz); } diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownDialog.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownDialog.java deleted file mode 100644 index 16e8c57747c4..000000000000 --- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownDialog.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.python.refactoring.classes.pushDown; - -import com.intellij.openapi.project.Project; -import com.intellij.refactoring.RefactoringBundle; -import com.intellij.refactoring.classMembers.MemberInfoChange; -import com.intellij.refactoring.classMembers.MemberInfoModel; -import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel; -import com.intellij.refactoring.ui.RefactoringDialog; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyElement; -import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; -import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage; -import com.jetbrains.python.refactoring.classes.ui.PyMemberSelectionPanel; - -import javax.swing.*; -import java.awt.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * @author Dennis.Ushakov - */ -public class PyPushDownDialog extends RefactoringDialog { - private final List myMemberInfos; - private final PyClass myClass; - private MemberInfoModel myMemberInfoModel; - - public PyPushDownDialog(Project project, PyClass aClass, PyMemberInfoStorage memberInfos) { - super(project, true); - myMemberInfos = memberInfos.getClassMemberInfos(aClass); - myClass = aClass; - - setTitle(PyPushDownHandler.REFACTORING_NAME); - - init(); - } - - protected String getHelpId() { - return "python.reference.pushMembersDown"; - } - - @Override - protected void doAction() { - if(!isOKActionEnabled()) return; - - final PyPushDownProcessor processor = new PyPushDownProcessor(getProject(), myClass, getSelectedMemberInfos()); - invokeRefactoring(processor); - } - - protected JComponent createNorthPanel() { - GridBagConstraints gbConstraints = new GridBagConstraints(); - - JPanel panel = new JPanel(new GridBagLayout()); - - gbConstraints.insets = new Insets(4, 0, 4, 8); - gbConstraints.weighty = 1; - gbConstraints.weightx = 1; - gbConstraints.gridy = 0; - gbConstraints.gridwidth = GridBagConstraints.REMAINDER; - gbConstraints.fill = GridBagConstraints.BOTH; - gbConstraints.anchor = GridBagConstraints.WEST; - final String fqn = myClass.getName(); - panel.add(new JLabel(RefactoringBundle.message("push.members.from.0.down.label", fqn)), gbConstraints); - return panel; - } - - protected JComponent createCenterPanel() { - JPanel panel = new JPanel(new BorderLayout()); - final PyMemberSelectionPanel memberSelectionPanel = new PyMemberSelectionPanel( - RefactoringBundle.message("members.to.be.pushed.down.panel.title"), - myMemberInfos, null); - panel.add(memberSelectionPanel, BorderLayout.CENTER); - - myMemberInfoModel = new UsedByDependencyMemberInfoModel(myClass); - myMemberInfoModel.memberInfoChanged(new MemberInfoChange(myMemberInfos)); - memberSelectionPanel.init(myMemberInfoModel, myMemberInfos); - return panel; - } - - public Collection getSelectedMemberInfos() { - ArrayList list = new ArrayList(myMemberInfos.size()); - for (PyMemberInfo info : myMemberInfos) { - if (info.isChecked() && myMemberInfoModel.isMemberEnabled(info)) { - list.add(info); - } - } - return list; - } -} diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java index d5684a51df6e..bd5c3088f6ca 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownHandler.java @@ -20,10 +20,14 @@ import com.intellij.openapi.project.Project; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.Query; +import com.jetbrains.python.PyBundle; import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.search.PyClassInheritorsSearch; import com.jetbrains.python.refactoring.classes.PyClassRefactoringHandler; import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage; +import com.jetbrains.python.vp.Creator; +import com.jetbrains.python.vp.ViewPresenterUtils; import org.jetbrains.annotations.NotNull; /** @@ -33,12 +37,12 @@ public class PyPushDownHandler extends PyClassRefactoringHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("push.members.down.title"); @Override - protected void doRefactorImpl(@NotNull Project project, - @NotNull PyClass classUnderRefactoring, - @NotNull PyMemberInfoStorage infoStorage, + protected void doRefactorImpl(@NotNull final Project project, + @NotNull final PyClass classUnderRefactoring, + @NotNull final PyMemberInfoStorage infoStorage, @NotNull Editor editor) { - + //TODO: Move to presenter? final Query query = PyClassInheritorsSearch.search(classUnderRefactoring, false); if (query.findFirst() == null) { final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", classUnderRefactoring.getName()); @@ -46,8 +50,19 @@ public class PyPushDownHandler extends PyClassRefactoringHandler { return; } - final PyPushDownDialog dialog = new PyPushDownDialog(project, classUnderRefactoring, infoStorage); - dialog.show(); + ViewPresenterUtils.linkViewWithPresenterAndLaunch(PyPushDownPresenter.class, PyPushDownView.class, new Creator() { + @NotNull + @Override + public PyPushDownPresenter createPresenter(@NotNull PyPushDownView view) { + return new PyPushDownPresenterImpl(view, classUnderRefactoring, infoStorage); + } + + @NotNull + @Override + public PyPushDownView createView(@NotNull PyPushDownPresenter presenter) { + return new PyPushDownViewSwingImpl(classUnderRefactoring, project, presenter); + } + }); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenter.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenter.java new file mode 100644 index 000000000000..357aa66009ad --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenter.java @@ -0,0 +1,9 @@ +package com.jetbrains.python.refactoring.classes.pushDown; + +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenter; + +/** + * @author Ilya.Kazakevich + */ +public interface PyPushDownPresenter extends MembersBasedPresenter { +} diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenterImpl.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenterImpl.java new file mode 100644 index 000000000000..3168638e45e9 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownPresenterImpl.java @@ -0,0 +1,48 @@ +package com.jetbrains.python.refactoring.classes.pushDown; + +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.BaseRefactoringProcessor; +import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel; +import com.intellij.util.containers.MultiMap; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.psi.PyUtil; +import com.jetbrains.python.psi.search.PyClassInheritorsSearch; +import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage; +import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenterWithPreviewImpl; +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +/** + * Created by Ilya.Kazakevich on 10.02.14. + */ +public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImpl implements PyPushDownPresenter { + public PyPushDownPresenterImpl(@NotNull PyPushDownView view, + @NotNull PyClass classUnderRefactoring, + @NotNull PyMemberInfoStorage infoStorage) { + super(view, classUnderRefactoring, infoStorage); + } + + @NotNull + @Override + public BaseRefactoringProcessor createProcessor() { + return new PyPushDownProcessor(myView.getSelectedMemberInfos(), myClassUnderRefactoring); + } + + @NotNull + @Override + protected MultiMap getConflicts() { + return new PyPushDownConflicts(myClassUnderRefactoring, myStorage.getClassMemberInfos(myClassUnderRefactoring)).getConflicts(); + } + + @Override + public void launch() { + UsedByDependencyMemberInfoModel model = + new UsedByDependencyMemberInfoModel(myClassUnderRefactoring); + myView.configure(new MembersViewInitializationInfo(model, PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring)))); + myView.initAndShow(); + } +} diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java index ec321f7f5999..713da65c17c1 100644 --- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownProcessor.java @@ -15,120 +15,47 @@ */ package com.jetbrains.python.refactoring.classes.pushDown; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Ref; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiNamedElement; -import com.intellij.refactoring.BaseRefactoringProcessor; import com.intellij.refactoring.RefactoringBundle; -import com.intellij.usageView.UsageInfo; -import com.intellij.usageView.UsageViewDescriptor; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.usageView.UsageViewBundle; import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyElement; -import com.jetbrains.python.psi.PyExpression; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.search.PyClassInheritorsSearch; -import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; +import com.jetbrains.python.refactoring.classes.membersManager.PyMembersRefactoringBaseProcessor; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.Collection; /** * @author Dennis.Ushakov */ -public class PyPushDownProcessor extends BaseRefactoringProcessor { - private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName()); +public class PyPushDownProcessor extends PyMembersRefactoringBaseProcessor { - private PyClass myClass; - private final Collection mySelectedMemberInfos; + private static final String HEADER = RefactoringBundle.message("push.down.members.elements.header"); - public PyPushDownProcessor(Project project, PyClass clazz, Collection selectedMemberInfos) { - super(project); - myClass = clazz; - mySelectedMemberInfos = selectedMemberInfos; + public PyPushDownProcessor(@NotNull Collection membersToMove, + @NotNull PyClass from) { + super(membersToMove, from, getChildren(from)); } @NotNull - @Override - protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) { - return new PyPushDownUsageViewDescriptor(myClass); + private static PyClass[] getChildren(@NotNull final PyClass from) { + Collection all = PyClassInheritorsSearch.search(from, false).findAll(); + return all.toArray(new PyClass[all.size()]); } - @NotNull - @Override - protected UsageInfo[] findUsages() { - final Collection subClasses = PyClassInheritorsSearch.search(myClass, false).findAll(); - final UsageInfo[] result = new UsageInfo[subClasses.size()]; - ContainerUtil.map2Array(subClasses, result, new Function() { - public UsageInfo fun(PyClass pyClass) { - return new UsageInfo(pyClass); - } - }); - return result; + + public String getProcessedElementsHeader() { + return HEADER; } - @Override - protected void refreshElements(PsiElement[] elements) { - if (elements.length == 1 && elements[0] instanceof PyClass) { - myClass = (PyClass)elements[0]; - } + public String getCodeReferencesText(int usagesCount, int filesCount) { + return RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount)); } - @Override - protected void performRefactoring(UsageInfo[] usages) { - final Set superClasses = new HashSet(); - final Set extractedClasses = new HashSet(); - final List methods = new ArrayList(); - for (PyMemberInfo member : mySelectedMemberInfos) { - final PyElement element = member.getMember(); - if (element instanceof PyFunction) methods.add((PyFunction)element); - else if (element instanceof PyClass) { - superClasses.add(element.getName()); - extractedClasses.add((PyClass)element); - } - else LOG.error("unmatched member class " + element.getClass()); - } - final PyElement[] elements = methods.toArray(new PyElement[methods.size()]); - - final List superClassesElements = PyClassRefactoringUtil.removeAndGetSuperClasses(myClass, superClasses); - - for (UsageInfo usage : usages) { - final PyClass targetClass = (PyClass)usage.getElement(); - PyClassRefactoringUtil.addMethods(targetClass, elements, false); - PyClassRefactoringUtil.addSuperclasses(myClass.getProject(), targetClass, superClassesElements, superClasses); - PyClassRefactoringUtil.insertImport(targetClass, extractedClasses); - } - - if (methods.size() != 0) { - PyPsiUtils.removeElements(elements); - PyClassRefactoringUtil.insertPassIfNeeded(myClass); - } - } - - @Override - protected boolean preprocessUsages(Ref ref) { - final UsageInfo[] usages = ref.get(); - final PyPushDownConflicts conflicts = new PyPushDownConflicts(myClass, mySelectedMemberInfos); - conflicts.checkSourceClassConflicts(); - - if (usages.length == 0) { - final String message = RefactoringBundle.message("class.0.does.not.have.inheritors", myClass.getName()) + "\nPushing members down will result in them being deleted"; - final int answer = Messages.showYesNoDialog(message, PyPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon()); - if (answer != Messages.YES) { - return false; - } - } - - for (UsageInfo usage : usages) { - conflicts.checkTargetClassConflicts((PyClass)usage.getElement()); - } - return showConflicts(conflicts.getConflicts(), usages); + @Nullable + public String getCommentReferencesText(int usagesCount, int filesCount) { + return null; } @Override diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownUsageViewDescriptor.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownUsageViewDescriptor.java deleted file mode 100644 index 97f858a386a8..000000000000 --- a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownUsageViewDescriptor.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2000-2013 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.python.refactoring.classes.pushDown; - -import com.intellij.psi.PsiElement; -import com.intellij.refactoring.RefactoringBundle; -import com.intellij.usageView.UsageViewBundle; -import com.intellij.usageView.UsageViewDescriptor; -import com.jetbrains.python.psi.PyClass; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * @author Dennis.Ushakov - */ -public class PyPushDownUsageViewDescriptor implements UsageViewDescriptor { - private final PyClass myClass; - private static final String HEADER = RefactoringBundle.message("push.down.members.elements.header"); - - public PyPushDownUsageViewDescriptor(final PyClass clazz) { - myClass = clazz; - } - - @NotNull - public PsiElement[] getElements() { - return new PsiElement[] {myClass}; - } - - public String getProcessedElementsHeader() { - return HEADER; - } - - public String getCodeReferencesText(int usagesCount, int filesCount) { - return RefactoringBundle.message("classes.to.push.down.members.to", UsageViewBundle.getReferencesString(usagesCount, filesCount)); - } - - @Nullable - public String getCommentReferencesText(int usagesCount, int filesCount) { - return null; - } -} diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownView.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownView.java new file mode 100644 index 000000000000..5b1848e9763b --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownView.java @@ -0,0 +1,10 @@ +package com.jetbrains.python.refactoring.classes.pushDown; + +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView; +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo; + +/** + * @author Ilya.Kazakevich + */ +public interface PyPushDownView extends MembersBasedView { +} diff --git a/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownViewSwingImpl.java b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownViewSwingImpl.java new file mode 100644 index 000000000000..b95272bdfd48 --- /dev/null +++ b/python/src/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownViewSwingImpl.java @@ -0,0 +1,26 @@ +package com.jetbrains.python.refactoring.classes.pushDown; + +import com.intellij.openapi.project.Project; +import com.intellij.refactoring.RefactoringBundle; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedViewSwingImpl; +import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo; +import org.jetbrains.annotations.NotNull; + +import java.awt.*; + +/** + * Created by Ilya.Kazakevich on 10.02.14. + */ +public class PyPushDownViewSwingImpl extends MembersBasedViewSwingImpl + implements PyPushDownView { + public PyPushDownViewSwingImpl( + @NotNull final PyClass classUnderRefactoring, + @NotNull final Project project, + @NotNull final PyPushDownPresenter presenter) { + super(project, presenter, RefactoringBundle.message("push.members.from.0.down.label", classUnderRefactoring.getName())); + + myCenterPanel.add(myPyMemberSelectionPanel, BorderLayout.CENTER); + setTitle(PyPushDownHandler.REFACTORING_NAME); + } +} diff --git a/python/src/com/jetbrains/python/vp/Presenter.java b/python/src/com/jetbrains/python/vp/Presenter.java index 59f06a15a015..251aff608200 100644 --- a/python/src/com/jetbrains/python/vp/Presenter.java +++ b/python/src/com/jetbrains/python/vp/Presenter.java @@ -23,6 +23,7 @@ package com.jetbrains.python.vp; public interface Presenter { /** * Launches dialog. Presenter should fetch data and start view. + * TODO: Say you run initand show and launch */ void launch(); } diff --git a/python/testData/refactoring/pullup/moveInstanceAttributesLeaveEmptyInit.after.py b/python/testData/refactoring/pullup/moveInstanceAttributesLeaveEmptyInit.after.py index 7b2db16882a6..b3793334f587 100644 --- a/python/testData/refactoring/pullup/moveInstanceAttributesLeaveEmptyInit.after.py +++ b/python/testData/refactoring/pullup/moveInstanceAttributesLeaveEmptyInit.after.py @@ -1,12 +1,12 @@ class Parent(object): C = 12 - def foo(self): - pass - def __init__(self): self.foo = 12 + def foo(self): + pass + class Child(Parent, object): def __init__(self): pass diff --git a/python/testData/refactoring/pullup/withComments.after.py b/python/testData/refactoring/pullup/withComments.after.py index ce91117e5413..d2d920f75a51 100644 --- a/python/testData/refactoring/pullup/withComments.after.py +++ b/python/testData/refactoring/pullup/withComments.after.py @@ -2,8 +2,10 @@ class Foo: def foo(self): print("a") - # this is boo def boo(self): + """ + In python we use multi-line comments inside of method body + """ print "rrrrr" diff --git a/python/testData/refactoring/pullup/withComments.py b/python/testData/refactoring/pullup/withComments.py index 32edbc391bf2..a044302c006b 100644 --- a/python/testData/refactoring/pullup/withComments.py +++ b/python/testData/refactoring/pullup/withComments.py @@ -3,6 +3,8 @@ class Foo: print("a") class Boo(Foo): - # this is boo def boo(self): + """ + In python we use multi-line comments inside of method body + """ print "rrrrr" \ No newline at end of file diff --git a/python/testData/refactoring/pushdown/existingmethod.after.py b/python/testData/refactoring/pushdown/existingmethod.after.py index 25daa38234d6..b396cd5c02e3 100644 --- a/python/testData/refactoring/pushdown/existingmethod.after.py +++ b/python/testData/refactoring/pushdown/existingmethod.after.py @@ -1,5 +1,7 @@ class Foo: + pass -class Boo(): + +class Boo(Foo): def foo(self): print "rrrrr" \ No newline at end of file diff --git a/python/testData/refactoring/pushdown/full.after.py b/python/testData/refactoring/pushdown/full.after.py new file mode 100644 index 000000000000..0845450d0805 --- /dev/null +++ b/python/testData/refactoring/pushdown/full.after.py @@ -0,0 +1,48 @@ +class Dummny(object): + pass + + +class Parent(object): + """ + This class is usefull. + """ + CLASS_VAR_2 = 2 + + def __init__(self): pass + + + def method_2(self): + i = 1 + + +class Child_1(Parent, Dummny): + CLASS_VAR_1 = 1 + + def __init__(self): + self.inst_var = 12 + self.bar = 64 + + def method_1(self): + """ + Some text + """ + pass + + +class Child_2(Parent, Dummny): + """ + This class implements most sophisticated algorithm + """ + CLASS_VAR_1 = 1 + + def __init__(self): + self.inst_var = 12 + + def lala(self): + pass + + def method_1(self): + """ + Some text + """ + pass diff --git a/python/testData/refactoring/pushdown/full.before.py b/python/testData/refactoring/pushdown/full.before.py new file mode 100644 index 000000000000..30b205bb13d2 --- /dev/null +++ b/python/testData/refactoring/pushdown/full.before.py @@ -0,0 +1,37 @@ +class Dummny(object): + pass + + +class Parent(object, Dummny): + """ + This class is usefull. + """ + CLASS_VAR_1 = 1 + CLASS_VAR_2 = 2 + + def __init__(self): + self.inst_var = 12 + + + def method_1(self): + """ + Some text + """ + pass + + def method_2(self): + i = 1 + + +class Child_1(Parent): + def __init__(self): + self.bar = 64 + pass + + +class Child_2(Parent): + """ + This class implements most sophisticated algorithm + """ + def lala(self): + pass diff --git a/python/testSrc/com/jetbrains/python/refactoring/classes/PyClassRefactoringTest.java b/python/testSrc/com/jetbrains/python/refactoring/classes/PyClassRefactoringTest.java index 7e4dce2c6fdd..c232752b85d1 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/classes/PyClassRefactoringTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/classes/PyClassRefactoringTest.java @@ -15,18 +15,25 @@ */ package com.jetbrains.python.refactoring.classes; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.project.Project; +import com.intellij.refactoring.BaseRefactoringProcessor; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyTargetExpression; import com.jetbrains.python.psi.stubs.PyClassNameIndex; +import com.jetbrains.python.refactoring.classes.membersManager.MembersManager; import org.hamcrest.Matchers; import org.jetbrains.annotations.NotNull; import org.junit.Assert; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; /** * @author Dennis.Ushakov @@ -76,4 +83,19 @@ public abstract class PyClassRefactoringTest extends PyTestCase { Assert.assertThat(String.format("Expected one class named %s", name), classes, Matchers.hasSize(1)); return classes.iterator().next(); } + + + protected void moveViaProcessor(@NotNull Project project, @NotNull final BaseRefactoringProcessor processor) { + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + processor.run(); + } + }); + } + }, null, null); + } } diff --git a/python/testSrc/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassTest.java b/python/testSrc/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassTest.java index 0322c71365f2..d872955a9b2b 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/classes/extractSuperclass/PyExtractSuperclassTest.java @@ -15,6 +15,8 @@ */ package com.jetbrains.python.refactoring.classes.extractSuperclass; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -30,6 +32,7 @@ import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -137,6 +140,7 @@ public class PyExtractSuperclassTest extends PyClassRefactoringTest { new WriteCommandAction.Simple(myFixture.getProject()) { @Override protected void run() throws Throwable { + //TODO: Test via presenter //noinspection ConstantConditions final String path = base_dir.getPath() + "/a/b"; PyExtractSuperclassHelper.extractSuperclass(clazz, members, superclassName, path + "/foo.py"); diff --git a/python/testSrc/com/jetbrains/python/refactoring/classes/PyPullUpTest.java b/python/testSrc/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpTest.java similarity index 83% rename from python/testSrc/com/jetbrains/python/refactoring/classes/PyPullUpTest.java rename to python/testSrc/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpTest.java index 322d1863f9de..4552c06e2b10 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/classes/PyPullUpTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/classes/pullUp/PyPullUpTest.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.refactoring.classes; +package com.jetbrains.python.refactoring.classes.pullUp; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.command.CommandProcessor; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.refactoring.classes.PyClassRefactoringTest; import com.jetbrains.python.refactoring.classes.membersManager.MembersManager; import java.util.Collections; @@ -66,6 +65,7 @@ public class PyPullUpTest extends PyClassRefactoringTest { public void testMoveInstanceAttributesNoInit() { doHelperTest("Child", "#instance_field", "Parent"); } + public void testMoveInstanceAttributesLeaveEmptyInit() { doHelperTest("Child", "#foo", "Parent"); } @@ -97,16 +97,7 @@ public class PyPullUpTest extends PyClassRefactoringTest { final PyClass clazz = findClass(className); final PyElement member = findMember(className, memberName); final PyClass superClass = findClass(superClassName); - CommandProcessor.getInstance().executeCommand(clazz.getProject(), new Runnable() { - @Override - public void run() { - ApplicationManager.getApplication().runWriteAction(new Runnable() { - @Override - public void run() { - MembersManager.moveAllMembers(clazz, superClass, Collections.singleton(MembersManager.findMember(clazz, member))); - } - }); - } - }, null, null); + moveViaProcessor(clazz.getProject(), + new PyPullUpProcessor(clazz, superClass, Collections.singleton(MembersManager.findMember(clazz, member)))); } } diff --git a/python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java b/python/testSrc/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownTest.java similarity index 81% rename from python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java rename to python/testSrc/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownTest.java index 480e3a4b4bf7..dec76bc608bc 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/classes/pushDown/PyPushDownTest.java @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.jetbrains.python.refactoring.classes; +package com.jetbrains.python.refactoring.classes.pushDown; import com.intellij.openapi.command.WriteCommandAction; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; +import com.jetbrains.python.refactoring.classes.PyClassRefactoringTest; import com.jetbrains.python.refactoring.classes.membersManager.MembersManager; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; import com.jetbrains.python.refactoring.classes.pushDown.PyPushDownProcessor; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -37,6 +39,10 @@ public class PyPushDownTest extends PyClassRefactoringTest { doProcessorTest("Zope", null, "Foo"); } + public void testFull() throws Exception { + doProcessorTest("Parent", null, "#CLASS_VAR_1", "#inst_var", ".method_1", "Dummny"); + } + public void testMultiple() throws Exception { doProcessorTest("Foo", null, ".foo"); } @@ -49,25 +55,20 @@ public class PyPushDownTest extends PyClassRefactoringTest { doProcessorTest("Foo", "method foo is already overridden in class Boo. Method will not be pushed down to that class.", ".foo"); } - private void doProcessorTest(final String className, final String expectedError, final String... membersName) throws Exception { + private void doProcessorTest(final String className, final String expectedError, final String... memberNames) throws Exception { try { String baseName = "/refactoring/pushdown/" + getTestName(true); myFixture.configureByFile(baseName + ".before.py"); final PyClass clazz = findClass(className); final List members = new ArrayList(); - for (String memberName : membersName) { + for (String memberName : memberNames) { final PyElement member = findMember(className, memberName); members.add(MembersManager.findMember(clazz, member)); } - final PyPushDownProcessor processor = new PyPushDownProcessor(myFixture.getProject(), clazz, members); - new WriteCommandAction.Simple(myFixture.getProject()) { - @Override - protected void run() throws Throwable { - processor.run(); - } - }.execute().throwException(); - myFixture.checkResultByFile(baseName + ".after.py"); + final PyPushDownProcessor processor = new PyPushDownProcessor(members, clazz); + moveViaProcessor(myFixture.getProject(), processor); + myFixture.checkResultByFile(baseName + ".after.py"); } catch (Exception e) { if (expectedError == null) throw e; assertTrue(e.getMessage(), e.getMessage().contains(expectedError));