package com.jetbrains.lang.dart.util;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.lang.dart.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DartClassResolveResult implements Cloneable {
public static final DartClassResolveResult EMPTY = new DartClassResolveResult(null);
private final @Nullable DartClass myDartClass;
private final @NotNull DartGenericSpecialization mySpecialization;
protected DartClassResolveResult(@Nullable final DartClass aClass) {
this(aClass, new DartGenericSpecialization());
}
protected DartClassResolveResult(@Nullable final DartClass aClass, @NotNull final DartGenericSpecialization specialization) {
myDartClass = aClass;
mySpecialization = specialization;
}
@Override
public DartClassResolveResult clone() {
return new DartClassResolveResult(myDartClass, mySpecialization.clone());
}
@NotNull
public static DartClassResolveResult create(@Nullable final DartClass dartClass) {
return create(dartClass, new DartGenericSpecialization());
}
@NotNull
public static DartClassResolveResult create(@Nullable final DartClass dartClass,
@NotNull final DartGenericSpecialization specialization) {
if (dartClass == null) {
return new DartClassResolveResult(null);
}
DartClassResolveResult resolveResult = DartClassResolveCache.getInstance(dartClass.getProject()).get(dartClass);
if (resolveResult == null) {
resolveResult = new DartClassResolveResult(dartClass);
DartClassResolveCache.getInstance(dartClass.getProject()).put(dartClass, resolveResult);
final DartType superClass = dartClass.getSuperClass();
if (superClass != null) {
final DartClassResolveResult result = DartResolveUtil.resolveClassByType(superClass);
result.specializeByParameters(superClass.getTypeArguments());
resolveResult.merge(result.getSpecialization());
}
for (DartType dartType : DartResolveUtil.getImplementsAndMixinsList(dartClass)) {
final DartClassResolveResult result = DartResolveUtil.resolveClassByType(dartType);
result.specializeByParameters(dartType.getTypeArguments());
resolveResult.merge(result.getSpecialization());
}
}
final DartClassResolveResult clone = resolveResult.clone();
clone.softMerge(specialization);
return clone;
}
private void merge(@NotNull final DartGenericSpecialization otherSpecializations) {
for (String key : otherSpecializations.map.keySet()) {
mySpecialization.map.put(key, otherSpecializations.map.get(key));
}
}
private void softMerge(@NotNull final DartGenericSpecialization otherSpecializations) {
for (String key : otherSpecializations.map.keySet()) {
if (!mySpecialization.map.containsKey(key)) {
mySpecialization.map.put(key, otherSpecializations.map.get(key));
}
}
}
@Nullable
public DartClass getDartClass() {
return myDartClass;
}
@NotNull
public DartGenericSpecialization getSpecialization() {
return mySpecialization;
}
public void specialize(@Nullable final PsiElement element) {
if (element == null || myDartClass == null || !myDartClass.isGeneric()) {
return;
}
if (element instanceof DartNewExpression) {
final DartType type = ((DartNewExpression)element).getType();
if (type != null) {
specializeByParameters(type.getTypeArguments());
}
}
}
public void specializeByParameters(@Nullable final DartTypeArguments typeArguments) {
if (typeArguments == null || myDartClass == null || !myDartClass.isGeneric()) {
return;
}
final DartTypeParameters parameters = myDartClass.getTypeParameters();
assert parameters != null;
final List<DartType> typeList = typeArguments.getTypeList().getTypeList();
for (int i = 0, size = parameters.getTypeParameterList().size(); i < size; i++) {
DartTypeParameter dartTypeParameter = parameters.getTypeParameterList().get(i);
DartComponentName componentName = dartTypeParameter == null ? null : dartTypeParameter.getComponentName();
final DartType specializedType = typeList.get(i);
if (componentName == null || specializedType == null) continue;
mySpecialization.put(myDartClass, componentName.getText(), DartResolveUtil.getDartClassResolveResult(specializedType,
mySpecialization));
}
specializeSupers(myDartClass, mySpecialization);
}
private static void specializeSupers(@Nullable final DartClass dartClass, @NotNull final DartGenericSpecialization specialization) {
if (dartClass == null) {
return;
}
final DartType superType = dartClass.getSuperClass();
if (superType != null) {
specializeSuperType(dartClass, specialization, superType);
}
for (DartType dartType : DartResolveUtil.getImplementsAndMixinsList(dartClass)) {
specializeSuperType(dartClass, specialization, dartType);
}
}
private static void specializeSuperType(@Nullable final DartClass dartClass,
@NotNull final DartGenericSpecialization specialization,
@NotNull final DartType type) {
final DartTypeArguments targetTypeArguments = type.getTypeArguments();
if (targetTypeArguments == null || dartClass == null || dartClass.getTypeParameters() == null) {
return;
}
final DartClassResolveResult typeTargetClassResolve = DartResolveUtil.resolveClassByType(type);
final DartClass typeTargetClass = typeTargetClassResolve.getDartClass();
if (typeTargetClass == null) {
return;
}
final DartTypeParameters typeTargetClassTypeParameters = typeTargetClass.getTypeParameters();
List<DartType> list = targetTypeArguments.getTypeList().getTypeList();
if (typeTargetClassTypeParameters == null || typeTargetClassTypeParameters.getTypeParameterList().size() != list.size()) {
return;
}
for (int i = 0, size = list.size(); i < size; i++) {
final DartType argumentType = list.get(i);
final PsiElement argumentTarget = argumentType.resolveReference();
if (argumentTarget == null ||
dartClass.getTypeParameters() != PsiTreeUtil.getParentOfType(argumentTarget, DartTypeParameters.class)) {
// argumentTarget isn't our generic
continue;
}
final String superGenericName = typeTargetClassTypeParameters.getTypeParameterList().get(i).getComponentName().getText();
final DartClassResolveResult resolveResult = specialization.get(typeTargetClass, superGenericName);
if (resolveResult == null || resolveResult.getDartClass() == null) {
specialization.put(typeTargetClass, superGenericName, specialization.get(dartClass, argumentTarget.getText()));
specializeSupers(typeTargetClass, specialization);
}
}
}
}