001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.types;
018    
019    import com.google.common.collect.LinkedHashMultimap;
020    import com.google.common.collect.Maps;
021    import com.google.common.collect.Multimap;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
025    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
026    
027    import java.util.List;
028    import java.util.Map;
029    
030    public class SubstitutionUtils {
031        private SubstitutionUtils() {
032        }
033    
034        /**
035         * Builds a context with all the supertypes' parameters substituted
036         */
037        @NotNull
038        public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) {
039            Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap();
040            TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution);
041            // we use the mutability of the map here
042            fillInDeepSubstitutor(type, typeSubstitutor, substitution, null);
043            return typeSubstitutor;
044        }
045    
046        /**
047          For each supertype of a given type, we map type parameters to type arguments.
048    
049          For instance, we have the following class hierarchy:
050              trait Iterable<out T>
051              trait Collection<out E>: Iterable<E>
052              trait MyFooCollection<F>: Collection<Foo<F>>
053    
054          For MyFooCollection<out CharSequence>, the following multimap will be returned:
055              T declared in Iterable -> Foo<out CharSequence>
056              E declared in Collection -> Foo<out CharSequence>
057              F declared in MyFooCollection -> out CharSequence
058         */
059        @NotNull
060        public static Multimap<TypeConstructor, TypeProjection> buildDeepSubstitutionMultimap(@NotNull JetType type) {
061            Multimap<TypeConstructor, TypeProjection> fullSubstitution = LinkedHashMultimap.create();
062            Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap();
063            TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution);
064            // we use the mutability of the map here
065            fillInDeepSubstitutor(type, typeSubstitutor, substitution, fullSubstitution);
066            return fullSubstitution;
067        }
068    
069        // we use the mutability of the substitution map here
070        private static void fillInDeepSubstitutor(
071                @NotNull JetType context,
072                @NotNull TypeSubstitutor substitutor,
073                @NotNull Map<TypeConstructor, TypeProjection> substitution,
074                @Nullable Multimap<TypeConstructor, TypeProjection> fullSubstitution
075        ) {
076            List<TypeParameterDescriptor> parameters = context.getConstructor().getParameters();
077            List<TypeProjection> arguments = context.getArguments();
078    
079            if (parameters.size() != arguments.size()) {
080                throw new IllegalStateException();
081            }
082    
083            for (int i = 0; i < arguments.size(); i++) {
084                TypeProjection argument = arguments.get(i);
085                TypeParameterDescriptor parameter = parameters.get(i);
086    
087                TypeProjection substitute = substitutor.substitute(argument);
088                assert substitute != null;
089                substitution.put(parameter.getTypeConstructor(), substitute);
090                if (fullSubstitution != null) {
091                    fullSubstitution.put(parameter.getTypeConstructor(), substitute);
092                }
093            }
094            if (KotlinBuiltIns.isNothingOrNullableNothing(context)) return;
095            for (JetType supertype : context.getConstructor().getSupertypes()) {
096                fillInDeepSubstitutor(supertype, substitutor, substitution, fullSubstitution);
097            }
098        }
099    }