import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
+import com.intellij.util.Processor;
+import com.jetbrains.NotNullPredicate;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
* Custom (aka dynamic) type that delegates calls to some classes you pass to it.
* We say this this class <strong>mimics</strong> such classes.
* To be used for cases like "type()".
- * It optionally filters methods using {@link ParentsMemberFilter}
+ * It optionally filters methods using {@link Processor}
*
* @author Ilya.Kazakevich
*/
-public class PyCustomType implements PyClassLikeType, Predicate<RatedResolveResult> {
+public class PyCustomType implements PyClassLikeType {
@NotNull
private final List<PyClassLikeType> myTypesToMimic = new ArrayList<PyClassLikeType>();
@Nullable
- private final ParentsMemberFilter myFilter;
+ private final Processor<PyElement> myFilter;
private final boolean myInstanceType;
/**
* @param filter filter to filter methods from classes (may be null to do no filtering)
* @param instanceType if true, then this class implements instance (it reports it is not definition and returns "this
- * for {@link #toInstance()} call).
+ * for {@link #toInstance()} call). If false, <strong>calling this type creates similar type with instance=true</strong>
+ * (like ctor)
* @param typesToMimic types to "mimic": delegate calls to (must be one at least!)
*/
- public PyCustomType(@Nullable final ParentsMemberFilter filter,
+ public PyCustomType(@Nullable final Processor<PyElement> filter,
final boolean instanceType,
@NotNull final PyClassLikeType... typesToMimic) {
Preconditions.checkArgument(typesToMimic.length > 0, "Provide at least one class");
myFilter = filter;
- myTypesToMimic.addAll(Arrays.asList(typesToMimic));
+ myTypesToMimic.addAll(Collections2.filter(Arrays.asList(typesToMimic), NotNullPredicate.INSTANCE));
myInstanceType = instanceType;
}
@Override
public final PyClassLikeType toInstance() {
- return myInstanceType ? this : new PyCustomType(myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[myTypesToMimic.size()]));
+ return myInstanceType
+ ? this
+ : new PyCustomType(myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[myTypesToMimic.size()]));
}
final List<RatedResolveResult> globalResult = new ArrayList<RatedResolveResult>();
// Delegate calls to classes, we mimic but filter if filter is set.
- for (final PyClassLikeType parentType : myTypesToMimic) {
- final List<? extends RatedResolveResult> results = parentType.resolveMember(name, location, direction, resolveContext, inherited);
+ for (final PyClassLikeType typeToMimic : myTypesToMimic) {
+ final List<? extends RatedResolveResult> results = typeToMimic.resolveMember(name, location, direction, resolveContext, inherited);
if (results != null) {
- globalResult.addAll(Collections2.filter(results, this));
+ globalResult.addAll(Collections2.filter(results, new ResolveFilter()));
}
}
return globalResult;
@Override
public final boolean isValid() {
+ for (final PyClassLikeType type : myTypesToMimic) {
+ if (!type.isValid()) {
+ return false;
+ }
+ }
+
return true;
}
@Override
public final boolean isCallable() {
- return true; // We do not know, actually
+ if (!myInstanceType) {
+ return true; // Due to ctor
+ }
+ for (final PyClassLikeType typeToMimic : myTypesToMimic) {
+ if (typeToMimic.isCallable()) {
+ return true;
+ }
+ }
+
+ return false;
}
@Nullable
}
- @Override
- public final boolean apply(@Nullable final RatedResolveResult input) {
- if (input == null) {
- return false;
- }
- if (myFilter == null) {
- return true; // No need to check
- }
- final PyElement pyElement = PyUtil.as(input.getElement(), PyElement.class);
- if (pyElement == null) {
- return false;
+ /**
+ * Predicate that filters resolve candidates using {@link #myFilter}
+ */
+ private class ResolveFilter implements Predicate<RatedResolveResult> {
+ @Override
+ public final boolean apply(@Nullable final RatedResolveResult input) {
+ if (input == null) {
+ return false;
+ }
+ if (myFilter == null) {
+ return true; // No need to check
+ }
+ final PyElement pyElement = PyUtil.as(input.getElement(), PyElement.class);
+ if (pyElement == null) {
+ return false;
+ }
+ return myFilter.process(pyElement);
}
- return myFilter.acceptMember(pyElement);
- }
-
- public interface ParentsMemberFilter {
- boolean acceptMember(@NotNull PyElement element);
}
/**
if (pyElement == null) {
return false;
}
- return myFilter.acceptMember(pyElement);
+ return myFilter.process(pyElement);
}
}
}
import com.intellij.util.ArrayUtil;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
-import com.intellij.util.containers.hash.HashMap;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.PyCustomMember;
import com.jetbrains.python.codeInsight.PyCustomMemberUtils;
}
resolving.add(key);
try {
- return doResolveMember(name, location, direction, resolveContext, inherited, null);
+ return doResolveMember(name, location, direction, resolveContext, inherited);
}
finally {
resolving.remove(key);
}
}
- /**
- * @param providerToSkip provider that should not be used (use it to prevent recursion). Pass null to use any provider.
- */
@Nullable
- public List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
- @Nullable PyExpression location,
- @NotNull AccessDirection direction,
- @NotNull PyResolveContext resolveContext,
- boolean inherited,
- @Nullable final PyClassMembersProvider providerToSkip) {
+ private List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
+ @Nullable PyExpression location,
+ @NotNull AccessDirection direction,
+ @NotNull PyResolveContext resolveContext,
+ boolean inherited) {
final TypeEvalContext context = resolveContext.getTypeEvalContext();
PsiElement classMember =
- resolveByOverridingMembersProviders(this, name, location,
- providerToSkip); //overriding members provers have priority to normal resolve
+ resolveByOverridingMembersProviders(this, name, location); //overriding members provers have priority to normal resolve
if (classMember != null) {
return ResolveResultList.to(classMember);
}
return ResolveResultList.to(classMember);
}
- classMember = resolveByOverridingAncestorsMembersProviders(this, name, location, providerToSkip);
+ classMember = resolveByOverridingAncestorsMembersProviders(this, name, location);
if (classMember != null) {
return ResolveResultList.to(classMember);
}
if (inherited) {
classMember =
- resolveByMembersProviders(this, name, location,
- providerToSkip); //ask providers after real class introspection as providers have less priority
+ resolveByMembersProviders(this, name, location); //ask providers after real class introspection as providers have less priority
}
if (classMember != null) {
for (PyClassLikeType type : myClass.getAncestorTypes(context)) {
if (type instanceof PyClassType) {
final PyClass pyClass = ((PyClassType)type).getPyClass();
- PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location, providerToSkip);
+ PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location);
if (superMember != null) {
return ResolveResultList.to(superMember);
return null;
}
- /**
- * @param providerToSkip provider that should not be used (use it to prevent recursion). Pass null to use any provider.
- */
@Nullable
- private static PsiElement resolveByMembersProviders(PyClassType aClass, String name,
- @Nullable PsiElement location,
- @Nullable final PyClassMembersProvider providerToSkip) {
+ private static PsiElement resolveByMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) {
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
- if (provider == providerToSkip) {
- continue;
- }
final PsiElement resolveResult = provider.resolveMember(aClass, name, location);
if (resolveResult != null) return resolveResult;
}
return null;
}
- /**
- * @param providerToSkip provider that should not be used (use it to prevent recursion). Pass null to use any provider.
- */
@Nullable
- private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location,
- @Nullable final PyClassMembersProvider providerToSkip) {
+ private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) {
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
- if (provider instanceof PyOverridingClassMembersProvider && provider != providerToSkip) {
+ if (provider instanceof PyOverridingClassMembersProvider) {
final PsiElement resolveResult = provider.resolveMember(aClass, name, location);
if (resolveResult != null) return resolveResult;
}
return null;
}
- /**
- * @param providerToSkip provider that should not be used (use it to prevent recursion). Pass null to use any provider.
- */
@Nullable
- private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location,
- @Nullable final PyClassMembersProvider providerToSkip) {
+ private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location) {
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
- if (provider instanceof PyOverridingAncestorsClassMembersProvider && provider != providerToSkip) {
+ if (provider instanceof PyOverridingAncestorsClassMembersProvider) {
final PsiElement resolveResult = provider.resolveMember(type, name, location);
if (resolveResult != null) return resolveResult;
}
if (namesAlready == null) {
namesAlready = new HashSet<String>();
}
-
- /**
- * [element_name] => (how_element_obtained, element_it_self).
- * To be used to replace elements with providers that may override them.
- */
- final Map<String, Pair<ElementType, Object>> usedNames = new HashMap<String, Pair<ElementType, Object>>();
-
+ List<Object> ret = new ArrayList<Object>();
boolean suppressParentheses = context.get(CTX_SUPPRESS_PARENTHESES) != null;
- addOwnClassMembers(location, namesAlready, suppressParentheses, usedNames);
- namesAlready.addAll(usedNames.keySet());
+ addOwnClassMembers(location, namesAlready, suppressParentheses, ret);
PsiFile origin = (location != null) ?
CompletionUtil.getOriginalOrSelf(location)
.getContainingFile() :
null;
final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(myClass.getProject(), origin);
- final List<Object> inheritedMembers = addInheritedMembers(prefix, location, namesAlready, context, usedNames, typeEvalContext);
- namesAlready.addAll(usedNames.keySet());
-
+ addInheritedMembers(prefix, location, namesAlready, context, ret, typeEvalContext);
- // TODO: extract method?
- // Adding elements from providers
+ // from providers
for (final PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
for (final PyCustomMember member : provider.getMembers(this, location)) {
final String name = member.getName();
- final LookupElementBuilder element = PyCustomMemberUtils.toLookUpElement(member, getName());
-
- final Pair<ElementType, Object> usedNameInfo = usedNames.get(name);
- /**
- * If element exists already (usedNameInfo != null) then we need to check if it is overwritable.
- * OWN elements could be overwritten by PyOverridingClassMembersProvider
- * INHERITED only by PyOverridingAncestorsClassMembersProvider
- */
- if (usedNameInfo == null ||
- (usedNameInfo.first == ElementType.INHERITED && provider instanceof PyOverridingAncestorsClassMembersProvider) ||
- (usedNameInfo.first == ElementType.OWN && provider instanceof PyOverridingClassMembersProvider)) {
- usedNames.put(name, Pair.<ElementType, Object>create(ElementType.BY_PROVIDER, element));
+ if (!namesAlready.contains(name)) {
+ ret.add(PyCustomMemberUtils.toLookUpElement(member, getName()));
}
}
}
-
- final List<Object> ret = new ArrayList<Object>(getElementsFromUsedNames(usedNames));
- namesAlready.addAll(usedNames.keySet());
- ret.addAll(inheritedMembers);
-
-
if (!myClass.isNewStyleClass()) {
final PyBuiltinCache cache = PyBuiltinCache.getInstance(myClass);
final PyClassType classobjType = cache.getOldstyleClassobjType();
return ret.toArray();
}
-
- private void addOwnClassMembers(PsiElement expressionHook,
- Set<String> namesAlready,
- boolean suppressParentheses,
- @NotNull final Map<String, Pair<ElementType, Object>> usedNames) {
+ private void addOwnClassMembers(PsiElement expressionHook, Set<String> namesAlready, boolean suppressParentheses, List<Object> ret) {
PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class);
if (containingClass != null) {
containingClass = CompletionUtil.getOriginalElement(containingClass);
if (namesAlready.contains(name)) continue;
if (!withinOurClass && isClassPrivate(name)) continue;
namesAlready.add(name);
- usedNames.put(name, Pair.<ElementType, Object>create(ElementType.OWN, le));
+ ret.add(le);
}
if (slots != null) {
for (String name : slots) {
if (!namesAlready.contains(name)) {
- usedNames.put(name, Pair.<ElementType, Object>create(ElementType.OWN, LookupElementBuilder.create(name)));
+ ret.add(LookupElementBuilder.create(name));
}
}
}
return false;
}
- @NotNull
- private List<Object> addInheritedMembers(String name,
- PsiElement expressionHook,
- Set<String> namesAlready,
- ProcessingContext context,
- @NotNull final Map<String, Pair<ElementType, Object>> usedNames,
- @NotNull TypeEvalContext typeEvalContext) {
- final List<Object> ret = new ArrayList<Object>();
+ private void addInheritedMembers(String name,
+ PsiElement expressionHook,
+ Set<String> namesAlready,
+ ProcessingContext context,
+ List<Object> ret,
+ @NotNull TypeEvalContext typeEvalContext) {
for (PyExpression expression : myClass.getSuperClassExpressions()) {
final PsiReference reference = expression.getReference();
PsiElement element = null;
for (Object ob : ancestry) {
String inheritedName = ob.toString();
if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) {
- usedNames.put(inheritedName, Pair.create(ElementType.INHERITED, ob));
+ ret.add(ob);
namesAlready.add(inheritedName);
}
}
ContainerUtil.addAll(ret, ancestry);
}
}
- return ret;
}
private static boolean isClassPrivate(String lookup_string) {
}
return new PyClassTypeImpl(pyClass, isDefinition);
}
-
- // TOOD: Doc
- private static List<Object> getElementsFromUsedNames(@NotNull final Map<String, Pair<ElementType, Object>> usedNames) {
- final List<Object> ret = new ArrayList<Object>();
- for (final Pair<ElementType, Object> objectPair : usedNames.values()) {
- ret.add(objectPair.second);
- }
- return ret;
- }
-
- /**
- * How class member was obtained
- */
- private enum ElementType {
- /**
- * Class own member
- */
- OWN,
- /**
- * Inherited from parent
- */
- INHERITED,
- /**
- * Added by provider (via extension point)
- */
- BY_PROVIDER
- }
}