blob: 142bb31e96d975c034776a69d80e0b386c964693 [file] [log] [blame]
package com.jetbrains.python.refactoring.classes.membersManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.MultiMap;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Plugin that moves class properties.
* It represents property (whatever old or new) as one of its methods.
*
* @author Ilya.Kazakevich
*/
class PropertiesManager extends MembersManager<PyElement> {
PropertiesManager() {
super(PyElement.class);
}
@NotNull
@Override
protected List<? extends PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
final List<PyElement> elements = new ArrayList<PyElement>(pyClass.getProperties().size());
for (final Property property : pyClass.getProperties().values()) {
elements.add(getElement(property));
}
return elements;
}
@NotNull
private static PyElement getElement(@NotNull final Property property) {
final Callable getter = property.getGetter().valueOrNull();
final Callable setter = property.getSetter().valueOrNull();
final Callable deleter = property.getDeleter().valueOrNull();
if (getter != null) {
return getter;
}
else if (setter != null) {
return setter;
}
else if (deleter != null) {
return deleter;
}
else {
final PyTargetExpression site = property.getDefinitionSite();
assert site != null : "Property has no methods nor declaration. That is not property";
return site;
}
}
@NotNull
private static Property getProperty(@NotNull final PyClass pyClass, @NotNull final PyElement element) {
final Collection<Property> properties = pyClass.getProperties().values();
if (element instanceof PyTargetExpression) {
return getPropertyByTargetExpression(properties, (PyTargetExpression)element);
}
if (element instanceof PyFunction) {
return getPropertyByFunction(properties, (PyFunction)element);
}
throw new IllegalArgumentException("Not function nor target");
}
@NotNull
private static Property getPropertyByFunction(@NotNull final Collection<Property> properties,
@NotNull final PyFunction functionToSearch) {
for (final Property property : properties) {
for (final PyFunction function : getAllFunctions(property)) {
if (function.equals(functionToSearch)) {
return property;
}
}
}
throw new IllegalArgumentException("No property found");
}
@NotNull
private static Property getPropertyByTargetExpression(@NotNull final Iterable<Property> properties,
@NotNull final PyTargetExpression element) {
for (final Property property : properties) {
if (element.equals(property.getDefinitionSite())) {
return property;
}
}
throw new IllegalArgumentException("No property found");
}
@NotNull
private static Collection<PyFunction> getAllFunctions(@NotNull final Property property) {
final Collection<PyFunction> result = new ArrayList<PyFunction>(3);
final Callable getter = property.getGetter().valueOrNull();
final Callable setter = property.getSetter().valueOrNull();
final Callable deleter = property.getDeleter().valueOrNull();
if (getter instanceof PyFunction) {
result.add((PyFunction)getter);
}
if (setter instanceof PyFunction) {
result.add((PyFunction)setter);
}
if (deleter instanceof PyFunction) {
result.add((PyFunction)deleter);
}
return result;
}
@Override
protected Collection<PyElement> moveMembers(@NotNull final PyClass from,
@NotNull final Collection<PyMemberInfo<PyElement>> members,
@NotNull final PyClass... to) {
final Collection<PyElement> result = new ArrayList<PyElement>();
final Collection<PyElement> elements = fetchElements(members);
for (final PyElement element : elements) {
final Property property = getProperty(from, element);
final Collection<PyFunction> functions = getAllFunctions(property);
MethodsManager.moveMethods(from, functions, false, to);
final PyTargetExpression definitionSite = property.getDefinitionSite();
if (definitionSite != null) {
final PyAssignmentStatement assignmentStatement = PsiTreeUtil.getParentOfType(definitionSite, PyAssignmentStatement.class);
ClassFieldsManager.moveAssignmentsImpl(from, Collections.singleton(assignmentStatement), to);
}
}
return result;
}
@NotNull
@Override
public PyMemberInfo<PyElement> apply(@NotNull final PyElement input) {
return new PyMemberInfo<PyElement>(input, false, getName(input), false, this, false);
}
private static String getName(@NotNull final PyElement input) {
final PyClass clazz = PsiTreeUtil.getParentOfType(input, PyClass.class);
assert clazz != null : "Element not declared in class";
final Property property = getProperty(clazz, input);
return property.getName();
}
@Override
public boolean hasConflict(@NotNull final PyElement member, @NotNull final PyClass aClass) {
return false;
}
@NotNull
@Override
protected MultiMap<PyClass, PyElement> getDependencies(@NotNull final PyElement member) {
final PyRecursiveElementVisitorWithResult visitor = new PyReferenceVisitor();
member.accept(visitor);
return visitor.myResult;
}
@NotNull
@Override
protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
return Collections.emptyList();
}
private static class PyReferenceVisitor extends PyRecursiveElementVisitorWithResult {
@Override
public void visitPyExpression(final PyExpression node) {
final PsiReference reference = node.getReference();
if (reference == null) {
return;
}
final PsiElement declaration = reference.resolve();
if (!(declaration instanceof PyFunction)) {
return;
}
final PyFunction function = (PyFunction)declaration;
final Property property = function.getProperty();
if (property == null) {
return;
}
final PyClass aClass = function.getContainingClass();
if (aClass == null) {
return;
}
final Collection<PyFunction> functions = getAllFunctions(property);
for (final PyFunction pyFunction : functions) {
final PyClass functionClass = pyFunction.getContainingClass();
if (functionClass != null) {
myResult.putValue(functionClass, pyFunction);
}
}
final PyTargetExpression definitionSite = property.getDefinitionSite();
if (definitionSite != null) {
final PyClass pyClass = PsiTreeUtil.getParentOfType(definitionSite, PyClass.class);
if (pyClass != null) {
myResult.putValue(pyClass, definitionSite);
}
}
}
}
}