Merge remote-tracking branch 'origin/master'

This commit is contained in:
Yann Cébron
2014-02-03 21:54:15 +01:00
21 changed files with 393 additions and 70 deletions
@@ -279,7 +279,7 @@ public class TemplateBuilderImpl implements TemplateBuilder {
public void run() {
final Project project = myFile.getProject();
VirtualFile file = myFile.getVirtualFile();
assert file != null;
assert file != null: "Virtual file is null for " + file;
OpenFileDescriptor descriptor = new OpenFileDescriptor(project, file);
final Editor editor = FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
@@ -155,6 +155,7 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
List<PyTargetExpression> getClassAttributes();
@Nullable
PyTargetExpression findClassAttribute(@NotNull String name, boolean inherited);
List<PyTargetExpression> getInstanceAttributes();
@@ -38,6 +38,7 @@ import org.jetbrains.annotations.NotNull;
import static com.jetbrains.python.psi.PyUtil.sure;
/**
* TODO: Refactor and move to {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#createMethod(String, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyFunction.Modifier, String...)}
* Adds a method foo to class X if X.foo() is unresolved.
* User: dcheryasov
* Date: Apr 5, 2009 6:51:26 PM
@@ -32,6 +32,7 @@ import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
import com.jetbrains.python.psi.impl.PyImportedModule;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
@@ -40,6 +41,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.jetbrains.python.psi.PyFunction.Modifier.CLASSMETHOD;
import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD;
/**
* @author Dennis.Ushakov
*/
@@ -140,15 +144,15 @@ public class PyClassRefactoringUtil {
}
/**
* Moves class field to another class
* Moves class field declarations to some other place
* @param expressions list of class fields
* @param superClass where to move them
*/
public static void moveFields(@NotNull final Collection<PyTargetExpression> expressions, @NotNull final PyClass superClass) {
public static void moveFieldDeclarationToStatement(@NotNull final Collection<PyTargetExpression> 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";
final PyStatementList superClassStatement = superClass.getStatementList();
PyUtil.addElementToStatementList(expAssignmentStatement.copy(), superClassStatement, true);
expAssignmentStatement.delete();
PyPsiUtils.removeRedundantPass(superClassStatement);
@@ -437,4 +441,41 @@ public class PyClassRefactoringUtil {
}
return null;
}
/**
* 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 parameterNames method parameters
* @return newly created method
*/
@NotNull
public static PyFunction createMethod(@NotNull final String methodName,
@NotNull final PyClass pyClass,
@Nullable final PyFunction.Modifier modifier,
@NotNull final String... parameterNames) {
final PyFunctionBuilder builder = new PyFunctionBuilder(methodName);
//TODO: Take names from codestyle?
if (modifier == null) {
builder.parameter(PyNames.CANONICAL_SELF);
}
else if (modifier == CLASSMETHOD) {
builder.parameter(PyNames.CANONICAL_CLS);
builder.decorate(PyNames.CLASSMETHOD);
}
else if (modifier == STATICMETHOD) {
builder.decorate(PyNames.STATICMETHOD);
}
for (final String parameterName : parameterNames) {
builder.parameter(parameterName);
}
final PyFunction function = builder.addFunction(pyClass.getStatementList(), LanguageLevel.getDefault());
addMethods(pyClass, new PyElement[]{function}, true);
return function;
}
}
@@ -1,57 +1,40 @@
package com.jetbrains.python.refactoring.classes.membersManager;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyAssignmentStatement;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Moves class attributes up
*
* @author Ilya.Kazakevich
*/
class ClassFieldsManager extends MembersManager<PyTargetExpression> {
class ClassFieldsManager extends FieldsManager {
ClassFieldsManager() {
super(PyTargetExpression.class);
super(true);
}
@Override
protected void moveMembers(@NotNull final PyClass from,
@NotNull final PyClass to,
@NotNull final Collection<PyTargetExpression> members) {
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, to.getStatementList());
}
@Override
protected boolean classHasField(@NotNull final PyClass pyClass, @NotNull final String fieldName) {
return pyClass.findClassAttribute(fieldName, true) != null;
}
@NotNull
@Override
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
return new ArrayList<PyElement>(Collections2.filter(pyClass.getClassAttributes(), new SimpleAssignmentsOnly()));
}
@Override
protected void moveMembers(@NotNull final PyClass from, @NotNull final PyClass to, @NotNull final Collection<PyTargetExpression> members) {
PyClassRefactoringUtil.moveFields(members, to);
}
@NotNull
@Override
public PyMemberInfo apply(@NotNull final PyElement input) {
return new PyMemberInfo(input, true, input.getText(), false, this); //TODO: Check overrides
}
private static class SimpleAssignmentsOnly implements Predicate<PyTargetExpression> {
//Support only simplest cases like CLASS_VAR = 42.
//Tuples (CLASS_VAR_1, CLASS_VAR_2) = "spam", "eggs" are not supported by now
@Override
public boolean apply(@Nullable final PyTargetExpression input) {
if (input == null) {
return false; //Filter out empties (which probably would never be here)
}
final PsiElement parent = input.getParent();
return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass());
}
protected List<PyTargetExpression> getFieldsByClass(@NotNull PyClass pyClass) {
return pyClass.getClassAttributes();
}
}
@@ -0,0 +1,96 @@
package com.jetbrains.python.refactoring.classes.membersManager;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyAssignmentStatement;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
/**
* Parent of all field-based plugins (like class fields, instance fields and so on)
* @author Ilya.Kazakevich
*/
abstract class FieldsManager extends MembersManager<PyTargetExpression> {
private static final SimpleAssignmentsOnly SIMPLE_ASSIGNMENTS_ONLY = new SimpleAssignmentsOnly();
private final boolean myStatic;
/**
* @param isStatic is field static or not?
*/
protected FieldsManager(final boolean isStatic) {
super(PyTargetExpression.class);
myStatic = isStatic;
}
@NotNull
@Override
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
return Lists.<PyElement>newArrayList(Collections2.filter(getFieldsByClass(pyClass), SIMPLE_ASSIGNMENTS_ONLY));
}
/**
* Checks if class has fields. Only child may know how to obtain field
* @param pyClass class to check
* @param fieldName field name
* @return true if has one
*/
protected abstract boolean classHasField(@NotNull PyClass pyClass, @NotNull String fieldName);
/**
* Returns all fields by class. Only child may know how to obtain fields
* @param pyClass class to check
* @return list of fields in target expression (declaration) form
*/
@NotNull
protected abstract List<PyTargetExpression> getFieldsByClass(@NotNull PyClass pyClass);
@NotNull
@Override
public PyMemberInfo apply(@NotNull final PyElement input) {
return new PyMemberInfo(input, myStatic, input.getText(), isOverrides((PyTargetExpression)input), this);
}
@Nullable
private Boolean isOverrides(@NotNull final PyTargetExpression input) {
final PyClass aClass = input.getContainingClass();
final String name = input.getName();
if (name == null) {
return null; //Field with out of name can't override something
}
assert aClass != null : "Target expression declared outside of class:" + input;
return classHasField(aClass, name) ? true : null;
}
@Override
protected void moveMembers(@NotNull final PyClass from,
@NotNull final PyClass to,
@NotNull final Collection<PyTargetExpression> members) {
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, to.getStatementList());
}
private static class SimpleAssignmentsOnly implements Predicate<PyTargetExpression> {
//Support only simplest cases like CLASS_VAR = 42.
//Tuples (CLASS_VAR_1, CLASS_VAR_2) = "spam", "eggs" are not supported by now
@Override
public boolean apply(@Nullable final PyTargetExpression input) {
if (input == null) {
return false; //Filter out empties (which probably would never be here)
}
final PsiElement parent = input.getParent();
return (parent != null) && PyAssignmentStatement.class.isAssignableFrom(parent.getClass());
}
}
}
@@ -0,0 +1,46 @@
package com.jetbrains.python.refactoring.classes.membersManager;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
/**
* @author Ilya.Kazakevich
*/
class InstanceFieldsManager extends FieldsManager {
InstanceFieldsManager() {
super(false);
}
@Override
protected void moveMembers(@NotNull final PyClass from,
@NotNull final PyClass to,
@NotNull final Collection<PyTargetExpression> members) {
//We need __init__ method, and if there is no any -- we need to create it
PyFunction initMethod = to.findMethodByName(PyNames.INIT, false);
if (initMethod == null) {
initMethod = PyClassRefactoringUtil.createMethod(PyNames.INIT, to, null);
}
final PyStatementList statementList = initMethod.getStatementList();
if (statementList == null) {
return; //TODO: Investigate how could it be
}
PyClassRefactoringUtil.moveFieldDeclarationToStatement(members, statementList);
}
@Override
protected boolean classHasField(@NotNull final PyClass pyClass, @NotNull final String fieldName) {
return pyClass.findInstanceAttribute(fieldName, true) != null;
}
@NotNull
@Override
protected List<PyTargetExpression> getFieldsByClass(@NotNull final PyClass pyClass) {
return pyClass.getInstanceAttributes();
}
}
@@ -2,13 +2,16 @@ package com.jetbrains.python.refactoring.classes.membersManager;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
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.PsiNamedElement;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyElement;
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;
@@ -28,7 +31,7 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
* List of managers. Class delegates all logic to them.
*/
private static final Collection<? extends MembersManager<?>> MANAGERS =
Arrays.asList(new MethodsManager(), new SuperClassesManager(), new ClassFieldsManager());
Arrays.asList(new MethodsManager(), new SuperClassesManager(), new ClassFieldsManager(), new InstanceFieldsManager());
private static final PyMemberExtractor PY_MEMBER_EXTRACTOR = new PyMemberExtractor();
@NotNull
@@ -114,6 +117,22 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
@NotNull
protected abstract List<PyElement> getMembersCouldBeMoved(@NotNull PyClass pyClass);
/**
* Filters out named elements (ones that subclasses {@link com.intellij.psi.PsiNamedElement}) and {@link com.jetbrains.python.psi.PyElement})
* that are null or has null name.
* You need it sometimes when code has errors (i.e. bad formatted code with annotation may treat annotation as method with null name.
* note: we should probably throw exceptions in such cases and display "refactoring not available" window in handler)
*
* @param elementsToFilter collection of elements to filter
* @param <T> element type
* @return collection of T with out of nulls and elemens whos {@link com.intellij.psi.PsiNamedElement#getName()} returns null
*/
@NotNull
protected static <T extends PsiNamedElement & PyElement> Collection<T> filterNameless(@NotNull final Collection<T> elementsToFilter) {
return Collections2.filter(elementsToFilter, new NamelessFilter<T>());
}
/**
* Moves element from one class to another
*
@@ -123,7 +142,12 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
*/
protected abstract void moveMembers(@NotNull PyClass from, @NotNull PyClass to, @NotNull Collection<T> members);
//TODO: Doc
/**
* Creates {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} from {@link com.jetbrains.python.psi.PyElement}
* This process is plugin-specific and should be implemented in each plugin
* @param input element
* @return member info
*/
@SuppressWarnings("NullableProblems") //IDEA-120100
@NotNull
@Override
@@ -136,6 +160,13 @@ public abstract class MembersManager<T extends PyElement> implements Function<Py
return input.getMember();
}
}
private static class NamelessFilter<T extends PyElement & PsiNamedElement> implements Predicate<T> {
@Override
public boolean apply(@Nullable final T input) {
return (input != null) && (input.getName() != null);
}
}
}
@@ -1,5 +1,6 @@
package com.jetbrains.python.refactoring.classes.membersManager;
import com.google.common.collect.Lists;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
import org.jetbrains.annotations.NotNull;
@@ -23,7 +24,7 @@ class MethodsManager extends MembersManager<PyFunction> {
@NotNull
@Override
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
return Arrays.<PyElement>asList(pyClass.getMethods());
return Lists.<PyElement>newArrayList(filterNameless(Arrays.asList(pyClass.getMethods())));
}
@Override
@@ -36,8 +37,10 @@ class MethodsManager extends MembersManager<PyFunction> {
public PyMemberInfo apply(@NotNull final PyElement input) {
//TODO: Use generics to prevent casting in each subclass
final PyFunction pyFunction = (PyFunction)input;
//TODO: Support static and classmethod functions
return new PyMemberInfo(input, false, buildDisplayMethodName(pyFunction), isOverrides(pyFunction), this);
final PyUtil.MethodFlags flags = PyUtil.MethodFlags.of(pyFunction);
assert flags != null : "No flags return while element is function " + pyFunction;
final boolean isStatic = flags.isStaticMethod() || flags.isClassMethod();
return new PyMemberInfo(input, isStatic, buildDisplayMethodName(pyFunction), isOverrides(pyFunction), this);
}
@Nullable
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.Nullable;
*/
public class PyMemberInfo extends MemberInfoBase<PyElement> {
@NotNull
private final MembersManager myMembersManager;
private final MembersManager<?> myMembersManager;
/**
* @param member element itself
@@ -39,7 +39,7 @@ public class PyMemberInfo extends MemberInfoBase<PyElement> {
final boolean isStatic,
@NotNull final String displayName,
@Nullable final Boolean overrides,
@NotNull final MembersManager membersManager) {
@NotNull final MembersManager<?> membersManager) {
super(member);
this.isStatic = isStatic;
this.displayName = displayName;
@@ -48,7 +48,7 @@ public class PyMemberInfo extends MemberInfoBase<PyElement> {
}
@NotNull
MembersManager getMembersManager() {
MembersManager<?> getMembersManager() {
return myMembersManager;
}
@@ -38,6 +38,7 @@ public class PyPullUpConflictsUtil {
for (PyMemberInfo info : infos) {
PsiElement member = info.getMember();
boolean isConflict = false;
//TODO: Delegate to MemeberManagers here
if (member instanceof PyFunction) {
final String name = ((PyFunction)member).getName();
if (name == null) continue;
@@ -31,6 +31,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* Pull-up presenter implementation
@@ -87,12 +88,13 @@ class PyPullUpPresenterImpl extends AbstractUsesDependencyMemberInfoModel<PyElem
public boolean isMemberEnabled(PyMemberInfo member) {
PyClass currentSuperClass = myView.getSelectedParent();
if (member.getMember() instanceof PyClass) {
//TODO: Delegate to Memebers Managers
PyClass memberClass = (PyClass)member.getMember();
if (memberClass.isSubclass(currentSuperClass) || currentSuperClass.isSubclass(memberClass)) {
return false; //Class is already parent of superclass
}
}
if (! PyPullUpConflictsUtil.checkConflicts(Arrays.asList(member), myView.getSelectedParent()).isEmpty()) {
if (! PyPullUpConflictsUtil.checkConflicts(Collections.singletonList(member), myView.getSelectedParent()).isEmpty()) {
return false; //Member has conflict
}
return (!myStorage.getDuplicatedMemberInfos(currentSuperClass).contains(member)) && member.getMember() != currentSuperClass;
@@ -59,6 +59,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* * TODO: Merge with {@link com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil#createMethod(String, com.jetbrains.python.psi.PyClass, com.jetbrains.python.psi.PyFunction.Modifier, String...)}
* @author oleg
*/
public class PyExtractMethodUtil {
@@ -0,0 +1,9 @@
class Parent:
def __init__(self):
self.instance_field = "eggs"
class Child(Parent):
def __init__(self):
Parent2.__init__(self)
@@ -0,0 +1,9 @@
class Parent:
pass
class Child(Parent):
def __init__(self):
Parent2.__init__(self)
self.instance_field = "eggs"
@@ -0,0 +1,9 @@
class Parent:
def __init__(self):
self.instance_field = "eggs"
class Child(Parent):
def __init__(self):
Parent2.__init__(self)
@@ -0,0 +1,10 @@
class Parent:
def __init__(self):
pass
class Child(Parent):
def __init__(self):
Parent2.__init__(self)
self.instance_field = "eggs"
@@ -29,10 +29,27 @@ class NoMembers(object):
class BadMro(MainParent, object, SubParent1, SubParent2):
pass
class SomeMembersDisabled(SubParent1, date): #SubParent1 is disabled
class HugeChild(SubParent1, date): #SubParent1 is disabled
def __init__(self):
self.instance_field_1 = 42
self.instance_field_2 = 100500
CLASS_FIELD = 42
(CLASS_FIELD_A,CLASS_FIELD_B) = (42,100500) We do not support tuples in class assignments for now (see ClassFieldsManager)
(CLASS_FIELD_A,CLASS_FIELD_B) = (42,100500) #We do not support tuples in class assignments for now (see ClassFieldsManager)
def foo(self): #should be disabled
pass
def bar(self):
pass
pass
@classmethod
def static_1(cls):
pass
@staticmethod
def static_2():
pass
@staticmethod
def bad_method(): #Code has errors, so method should be not be marked as static
pass
@@ -20,6 +20,7 @@ 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 org.hamcrest.Matchers;
import org.jetbrains.annotations.NotNull;
@@ -56,7 +57,12 @@ public abstract class PyClassRefactoringTest extends PyTestCase {
}
private PyElement findField(final String className, final String memberName) {
return findClass(className).findClassAttribute(memberName, false);
final PyClass aClass = findClass(className);
final PyTargetExpression attribute = aClass.findClassAttribute(memberName, false);
if (attribute != null) {
return attribute;
}
return aClass.findInstanceAttribute(memberName, false);
}
private PyFunction findMethod(final String className, final String name) {
@@ -57,6 +57,13 @@ public class PyPullUpTest extends PyClassRefactoringTest {
doHelperTest("Child2", "#CLASS_VAR", "Parent2");
}
public void testMoveInstanceAttributesSimple() {
doHelperTest("Child", "#instance_field", "Parent");
}
public void testMoveInstanceAttributesNoInit() {
doHelperTest("Child", "#instance_field", "Parent");
}
public void testMultiFile() { // PY-2810
doMultiFileTest();
}
@@ -2,18 +2,18 @@ package com.jetbrains.python.refactoring.classes.pullUp;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.intellij.openapi.util.Pair;
import com.intellij.refactoring.classMembers.MemberInfoModel;
import com.jetbrains.python.fixtures.PyTestCase;
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.membersManager.PyMemberInfo;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.internal.MocksControl;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
@@ -70,12 +70,14 @@ public class PyPullUpPresenterTest extends PyTestCase {
public void testNoParents() throws Exception {
ensureNoMembers("NoParentsAllowed");
}
/**
* Checks that refactoring does not work for classes with out of members
*/
public void testNoMembers() throws Exception {
ensureNoMembers("NoMembers");
}
/**
* Checks that refactoring does not work when C3 MRO can't be calculated
*/
@@ -84,10 +86,10 @@ public class PyPullUpPresenterTest extends PyTestCase {
}
/**
* Checks that some members are not allowed
* Checks that some members are not allowed, while others are
*/
public void testDisabledMembers() throws Exception {
PyPullUpPresenterImpl sut = configureByClass("SomeMembersDisabled");
public void testMembers() throws Exception {
PyPullUpPresenterImpl sut = configureByClass("HugeChild");
EasyMock.expect(myView.getSelectedParent()).andReturn(getClassByName("SubParent1")).anyTimes();
myMocksControl.replay();
@@ -96,16 +98,22 @@ public class PyPullUpPresenterTest extends PyTestCase {
Assert.assertTrue("No members selected", myMemberInfos.hasCaptured());
List<PyMemberInfo> members = myMemberInfos.getValue();
Assert.assertFalse("No members selected", members.isEmpty());
Collection<Pair<String, Boolean>> memberNamesAndStatus = Collections2.transform(members, new NameAndStatusTransformer(sut));
final Collection<Entry> memberNamesAndStatus = Collections2.transform(members, new NameAndStatusTransformer(sut));
//Pair will return correct type
@SuppressWarnings("unchecked") Matcher<Iterable<? extends Pair<String, Boolean>>> matcher = Matchers
.containsInAnyOrder(
Pair.create("date", true),
Pair.create("CLASS_FIELD", true),
Pair.create("SubParent1", false),
Pair.create("foo", false),
Pair.create("bar", true));
final Matcher<Iterable<? extends Entry>> matcher = Matchers
.containsInAnyOrder(new Entry("extends date", true, false),
new Entry("CLASS_FIELD", true, true),
new Entry("__init__(self)", true, false),
new Entry("extends SubParent1", false, false),
new Entry("foo(self)", false, false),
new Entry("bar(self)", true, false),
new Entry("static_1(cls)", true, true),
new Entry("static_2()", true, true),
new Entry("self.instance_field_1", true, false),
new Entry("self.instance_field_2", true, false),
new Entry("bad_method()", true, false)
);
Assert.assertThat("Wrong members or their states", memberNamesAndStatus, matcher);
@@ -128,7 +136,6 @@ public class PyPullUpPresenterTest extends PyTestCase {
}
private PyPullUpPresenterImpl configureByClass(String name) {
PyClass childClass = getClassByName(name);
PyMemberInfoStorage storage = new PyMemberInfoStorage(childClass);
@@ -148,7 +155,7 @@ public class PyPullUpPresenterTest extends PyTestCase {
}
}
private static class NameAndStatusTransformer implements Function<PyMemberInfo, Pair<String, Boolean>> {
private static class NameAndStatusTransformer implements Function<PyMemberInfo, Entry> {
private final PyPullUpPresenterImpl presenter;
private NameAndStatusTransformer(PyPullUpPresenterImpl presenter) {
@@ -156,9 +163,52 @@ public class PyPullUpPresenterTest extends PyTestCase {
}
@Override
public Pair<String, Boolean> apply(PyMemberInfo input) {
PyElement member = input.getMember();
return Pair.create(member.getName(), presenter.isMemberEnabled(input));
public Entry apply(final PyMemberInfo input) {
return new Entry(input.getDisplayName(), presenter.isMemberEnabled(input), input.isStatic());
}
}
private static class Entry {
@NonNls @NotNull
private final String myName;
private final boolean myEnabled;
private final boolean myStaticEntry;
private Entry(@NotNull final String name, final boolean enabled, final boolean staticEntry) {
myName = name;
myEnabled = enabled;
myStaticEntry = staticEntry;
}
@Override
public String toString() {
return "Entry{" +
"myName='" + myName + '\'' +
", myEnabled=" + myEnabled +
", myStaticEntry=" + myStaticEntry +
'}';
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof Entry)) return false;
final Entry entry = (Entry)o;
if (myEnabled != entry.myEnabled) return false;
if (myStaticEntry != entry.myStaticEntry) return false;
if (!myName.equals(entry.myName)) return false;
return true;
}
@Override
public int hashCode() {
int result = myName.hashCode();
result = 31 * result + (myEnabled ? 1 : 0);
result = 31 * result + (myStaticEntry ? 1 : 0);
return result;
}
}
}