/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.types;
import kotlin.collections.
CollectionsKt;
import org.jetbrains.annotations.
NotNull;
import org.jetbrains.kotlin.descriptors.
CallableDescriptor;
import org.jetbrains.kotlin.descriptors.
ClassifierDescriptor;
import org.jetbrains.kotlin.descriptors.
TypeParameterDescriptor;
import org.jetbrains.kotlin.utils.
DFS;
import java.util.
Collections;
import java.util.
HashMap;
import java.util.
List;
import java.util.
Map;
public class
BoundsSubstitutor {
private
BoundsSubstitutor() {
}
@
NotNull
public static <D extends
CallableDescriptor> D
substituteBounds(@
NotNull D
functionDescriptor) {
List<
TypeParameterDescriptor>
typeParameters =
functionDescriptor.
getTypeParameters();
if (
typeParameters.
isEmpty()) return
functionDescriptor;
// TODO: this does not handle any recursion in the bounds
@
SuppressWarnings("unchecked")
D
substitutedFunction = (D)
functionDescriptor.
substitute(
createUpperBoundsSubstitutor(
typeParameters));
assert
substitutedFunction != null : "Substituting upper bounds should always be legal";
return
substitutedFunction;
}
@
NotNull
public static <D extends
CallableDescriptor>
TypeSubstitutor createUpperBoundsSubstitutor(@
NotNull D
callableDescriptor) {
return
createUpperBoundsSubstitutor(
callableDescriptor.
getTypeParameters());
}
@
NotNull
private static
TypeSubstitutor createUpperBoundsSubstitutor(@
NotNull List<
TypeParameterDescriptor>
typeParameters) {
Map<
TypeConstructor,
TypeProjection>
mutableSubstitution = new
HashMap<>();
TypeSubstitutor substitutor =
TypeSubstitutor.
create(
mutableSubstitution);
// todo assert: no loops
for (
TypeParameterDescriptor descriptor :
topologicallySortTypeParameters(
typeParameters)) {
KotlinType upperBoundsAsType =
TypeIntersector.
getUpperBoundsAsType(
descriptor);
KotlinType substitutedUpperBoundsAsType =
substitutor.
substitute(
upperBoundsAsType,
Variance.
INVARIANT);
mutableSubstitution.
put(
descriptor.
getTypeConstructor(), new
TypeProjectionImpl(
substitutedUpperBoundsAsType));
}
return
substitutor;
}
@
NotNull
private static
List<
TypeParameterDescriptor>
topologicallySortTypeParameters(@
NotNull List<
TypeParameterDescriptor>
typeParameters) {
// In the end, we want every parameter to have no references to those after it in the list
// This gives us the reversed order: the one that refers to everybody else comes first
List<
TypeParameterDescriptor>
topOrder =
DFS.
topologicalOrder(
typeParameters,
current ->
getTypeParametersFromUpperBounds(
current,
typeParameters)
);
assert
topOrder.
size() ==
typeParameters.
size() : "All type parameters must be visited, but only " +
topOrder + " were";
// Now, the one that refers to everybody else stands in the last position
Collections.
reverse(
topOrder);
return
topOrder;
}
@
NotNull
private static
List<
TypeParameterDescriptor>
getTypeParametersFromUpperBounds(
@
NotNull TypeParameterDescriptor current,
@
NotNull List<
TypeParameterDescriptor>
typeParameters
) {
return
DFS.
dfs(
current.
getUpperBounds(),
typeParameter ->
CollectionsKt.
map(
typeParameter.
getArguments(),
TypeProjection::getType),
new
DFS.
NodeHandlerWithListResult<
KotlinType,
TypeParameterDescriptor>() {
@
Override
public boolean
beforeChildren(
KotlinType current) {
ClassifierDescriptor declarationDescriptor =
current.
getConstructor().
getDeclarationDescriptor();
// typeParameters in a list, but it contains very few elements, so it's fine to call contains() on it
//noinspection SuspiciousMethodCalls
if (
typeParameters.
contains(
declarationDescriptor)) {
result.
add((
TypeParameterDescriptor)
declarationDescriptor);
}
return true;
}
}
);
}
}