package org.jetbrains.jet.lang.types; import org.jetbrains.annotations.NotNull; import java.util.*; /** * @author abreslav */ public class TypeSubstitutor { public static final TypeSubstitutor INSTANCE = new TypeSubstitutor(); private TypeSubstitutor() {} public JetType substitute(@NotNull JetType context, @NotNull JetType subject, @NotNull Variance howThisTypeIsUsed) { return substitute(getSubstitutionContext(context), subject, howThisTypeIsUsed); } @NotNull public JetType substitute(@NotNull Map substitutionContext, @NotNull JetType type, @NotNull Variance howThisTypeIsUsed) { TypeProjection value = substitutionContext.get(type.getConstructor()); if (value != null) { Variance projectionKind = value.getProjectionKind(); if (howThisTypeIsUsed.allowsInPosition() && !projectionKind.allowsInPosition() || howThisTypeIsUsed.allowsOutPosition() && !projectionKind.allowsOutPosition()) { return ErrorType.createWrongVarianceErrorType(value); } return value.getType(); } return specializeType(type, substituteInArguments(substitutionContext, type)); } public Map getSubstitutionContext(JetType context) { List parameters = context.getConstructor().getParameters(); List contextArguments = context.getArguments(); return buildSubstitutionContext(parameters, contextArguments); } public Map buildSubstitutionContext(List parameters, List contextArguments) { Map parameterValues = new HashMap(); for (int i = 0, parametersSize = parameters.size(); i < parametersSize; i++) { TypeParameterDescriptor parameter = parameters.get(i); TypeProjection value = contextArguments.get(i); parameterValues.put(parameter.getTypeConstructor(), value); } return parameterValues; } @NotNull private TypeProjection substituteInProjection(Map parameterValues, TypeProjection subject) { @NotNull JetType subjectType = subject.getType(); TypeProjection value = parameterValues.get(subjectType.getConstructor()); if (value != null) { return value; } List newArguments = substituteInArguments(parameterValues, subjectType); return new TypeProjection(subject.getProjectionKind(), specializeType(subjectType, newArguments)); } private List substituteInArguments(Map parameterValues, JetType subjectType) { List newArguments = new ArrayList(); for (TypeProjection argument : subjectType.getArguments()) { newArguments.add(substituteInProjection(parameterValues, argument)); } return newArguments; } private JetType specializeType(JetType type, List newArguments) { return new JetTypeImpl(type.getAttributes(), type.getConstructor(), type.isNullable(), newArguments, type.getMemberScope()); } public Set substituteInSet(Map substitutionContext, Set types, Variance howTheseTypesWillBeUsed) { Set result = new HashSet(); for (JetType type : types) { result.add(substitute(substitutionContext, type, howTheseTypesWillBeUsed)); } return result; } }