package com.jetbrains.lang.dart.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.*;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.lang.dart.DartComponentType;
import com.jetbrains.lang.dart.psi.*;
import com.jetbrains.lang.dart.util.DartClassResolveResult;
import com.jetbrains.lang.dart.util.DartResolveUtil;
import gnu.trove.THashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
abstract public class AbstractDartPsiClass extends AbstractDartComponentImpl implements DartClass {
private CachedValue<Map<String, List<DartComponent>>> myMembersCache;
public AbstractDartPsiClass(@NotNull ASTNode node) {
super(node);
}
@Override
public boolean isEnum() {
return this instanceof DartEnumDefinition;
}
@NotNull
@Override
public List<DartEnumConstantDeclaration> getEnumConstantDeclarationList() {
return Collections.emptyList();
}
@Nullable
public DartTypeParameters getTypeParameters() {
return null;
}
@NotNull
public DartClassResolveResult getSuperClassResolvedOrObjectClass() {
if (DartResolveUtil.OBJECT.equals(getName())) return DartClassResolveResult.EMPTY;
final DartType superClass = getSuperClass();
return superClass != null ? DartResolveUtil.resolveClassByType(superClass)
: DartResolveUtil.findCoreClass(this, DartResolveUtil.OBJECT);
}
@Nullable
@Override
public DartType getSuperClass() {
final DartSuperclass superclass = PsiTreeUtil.getChildOfType(this, DartSuperclass.class);
if (superclass != null) return superclass.getType();
final DartMixinApplication mixinApp = PsiTreeUtil.getChildOfType(this, DartMixinApplication.class);
if (mixinApp != null) return mixinApp.getType();
return null;
}
@NotNull
@Override
public List<DartType> getImplementsList() {
final DartMixinApplication mixinApp = PsiTreeUtil.getChildOfType(this, DartMixinApplication.class);
final DartInterfaces interfaces = mixinApp != null ? mixinApp.getInterfaces() : PsiTreeUtil.getChildOfType(this, DartInterfaces.class);
if (interfaces != null) return DartResolveUtil.getTypes(interfaces.getTypeList());
return Collections.emptyList();
}
@NotNull
@Override
public List<DartType> getMixinsList() {
final DartMixinApplication mixinApp = PsiTreeUtil.getChildOfType(this, DartMixinApplication.class);
final DartMixins mixins = PsiTreeUtil.getChildOfType(mixinApp != null ? mixinApp : this, DartMixins.class);
if (mixins != null) {
return DartResolveUtil.getTypes(mixins.getTypeList());
}
return Collections.emptyList();
}
@Override
public boolean isGeneric() {
return getTypeParameters() != null;
}
@NotNull
@Override
public List<DartComponent> getMethods() {
final List<DartComponent> components = DartResolveUtil.findNamedSubComponents(this);
return DartResolveUtil.filterComponentsByType(components, DartComponentType.METHOD);
}
@NotNull
@Override
public List<DartComponent> getFields() {
final List<DartComponent> components = DartResolveUtil.findNamedSubComponents(this);
return DartResolveUtil.filterComponentsByType(components, DartComponentType.FIELD);
}
@NotNull
@Override
public List<DartComponent> getConstructors() {
final List<DartComponent> components = DartResolveUtil.getNamedSubComponents(this);
final String className = getName();
if (className == null) {
return Collections.emptyList();
}
return ContainerUtil.filter(components, component -> DartComponentType.typeOf(component) == DartComponentType.CONSTRUCTOR);
}
@Override
public List<DartMethodDeclaration> getOperators() {
return DartResolveUtil.findOperators(this);
}
@Nullable
@Override
public DartMethodDeclaration findOperator(final String operator, @Nullable final DartClass rightDartClass) {
return ContainerUtil.find(getOperators(), (Condition<PsiElement>)element -> {
if (element instanceof DartMethodDeclaration) {
final DartMethodDeclaration method = (DartMethodDeclaration)element;
if (method.isOperator() && operator.equals(method.getName())) {
if (rightDartClass == null) {
return true;
}
final DartFormalParameterList formalParameterList = PsiTreeUtil.getChildOfType(element, DartFormalParameterList.class);
return DartResolveUtil.checkParametersType(formalParameterList, rightDartClass);
}
}
return false;
});
}
@Override
public DartComponent findFieldByName(@NotNull final String name) {
return ContainerUtil.find(getFields(), component -> name.equals(component.getName()));
}
@Override
public DartComponent findMethodByName(@NotNull final String name) {
return ContainerUtil.find(getMethods(), component -> name.equals(component.getName()));
}
@Override
public DartComponent findMemberByName(@NotNull String name) {
final List<DartComponent> membersByName = findMembersByName(name);
return membersByName.isEmpty() ? null : membersByName.iterator().next();
}
@NotNull
@Override
public List<DartComponent> findMembersByName(@NotNull final String name) {
ensureMembersCacheInitialized();
final List<DartComponent> components = myMembersCache.getValue().get(name);
return components == null ? Collections.emptyList() : components;
}
private void ensureMembersCacheInitialized() {
if (myMembersCache == null) {
myMembersCache = CachedValuesManager.getManager(getProject()).createCachedValue(
() -> {
final Map<String, List<DartComponent>> nameToMembers = new THashMap<>();
for (DartComponent component : DartResolveUtil.findNamedSubComponents(false, this)) {
final String componentName = component.getName();
final DartClass dartClass = PsiTreeUtil.getParentOfType(component, DartClass.class);
final String dartClassName = dartClass != null ? dartClass.getName() : null;
if (dartClassName != null && dartClassName.equals(componentName)) {
continue;
}
List<DartComponent> components = nameToMembers.get(componentName);
if (components == null) {
components = new SmartList<>();
nameToMembers.put(componentName, components);
}
components.add(component);
}
return new CachedValueProvider.Result<>(nameToMembers, PsiModificationTracker.MODIFICATION_COUNT);
}, false);
}
}
@Override
public DartComponent findNamedConstructor(final String name) {
return ContainerUtil.find(getConstructors(), component -> name.equals(component.getName()));
}
}