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.resolve.jvm;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
022 import org.jetbrains.kotlin.descriptors.SourceElement;
023 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
024 import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
025 import org.jetbrains.kotlin.load.java.structure.*;
026 import org.jetbrains.kotlin.types.TypeConstructor;
027 import org.jetbrains.kotlin.types.TypeProjection;
028 import org.jetbrains.kotlin.types.TypeProjectionImpl;
029 import org.jetbrains.kotlin.types.TypeSubstitutor;
030
031 import java.util.*;
032
033 public class JavaResolverUtils {
034 private JavaResolverUtils() {
035 }
036
037 /**
038 * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType)
039 */
040 @Nullable
041 public static JavaType erasure(@NotNull JavaType type) {
042 return erasure(type, JavaTypeSubstitutor.EMPTY);
043 }
044
045 /**
046 * @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType, com.intellij.psi.PsiSubstitutor)
047 */
048 @Nullable
049 public static JavaType erasure(@NotNull JavaType type, @NotNull JavaTypeSubstitutor substitutor) {
050 if (type instanceof JavaClassifierType) {
051 JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
052 if (classifier instanceof JavaClass) {
053 return ((JavaClass) classifier).getDefaultType();
054 }
055 else if (classifier instanceof JavaTypeParameter) {
056 JavaTypeParameter typeParameter = (JavaTypeParameter) classifier;
057 return typeParameterErasure(typeParameter, new HashSet<JavaTypeParameter>(), substitutor);
058 }
059 else {
060 return null;
061 }
062 }
063 else if (type instanceof JavaPrimitiveType) {
064 return type;
065 }
066 else if (type instanceof JavaArrayType) {
067 JavaType erasure = erasure(((JavaArrayType) type).getComponentType(), substitutor);
068 return erasure == null ? null : erasure.createArrayType();
069 }
070 else if (type instanceof JavaWildcardType) {
071 JavaWildcardType wildcardType = (JavaWildcardType) type;
072 JavaType bound = wildcardType.getBound();
073 if (bound != null && wildcardType.isExtends()) {
074 return erasure(bound, substitutor);
075 }
076 return wildcardType.getTypeProvider().createJavaLangObjectType();
077 }
078 else {
079 throw new IllegalStateException("Unsupported type: " + type);
080 }
081 }
082
083 /**
084 * @see com.intellij.psi.util.TypeConversionUtil#typeParameterErasure(com.intellij.psi.PsiTypeParameter)
085 */
086 @Nullable
087 private static JavaType typeParameterErasure(
088 @NotNull JavaTypeParameter typeParameter,
089 @NotNull HashSet<JavaTypeParameter> visited,
090 @NotNull JavaTypeSubstitutor substitutor
091 ) {
092 Collection<JavaClassifierType> upperBounds = typeParameter.getUpperBounds();
093 if (!upperBounds.isEmpty()) {
094 JavaClassifier classifier = upperBounds.iterator().next().getClassifier();
095 if (classifier instanceof JavaTypeParameter && !visited.contains(classifier)) {
096 JavaTypeParameter typeParameterBound = (JavaTypeParameter) classifier;
097 visited.add(typeParameterBound);
098 JavaType substitutedType = substitutor.substitute(typeParameterBound);
099 if (substitutedType != null) {
100 return erasure(substitutedType);
101 }
102 return typeParameterErasure(typeParameterBound, visited, substitutor);
103 }
104 else if (classifier instanceof JavaClass) {
105 return ((JavaClass) classifier).getDefaultType();
106 }
107 }
108 return typeParameter.getTypeProvider().createJavaLangObjectType();
109 }
110
111 @NotNull
112 public static Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> recreateTypeParametersAndReturnMapping(
113 @NotNull List<TypeParameterDescriptor> originalParameters,
114 @Nullable DeclarationDescriptor newOwner
115 ) {
116 // LinkedHashMap to save the order of type parameters
117 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> result =
118 new LinkedHashMap<TypeParameterDescriptor, TypeParameterDescriptorImpl>();
119 for (TypeParameterDescriptor typeParameter : originalParameters) {
120 result.put(typeParameter,
121 TypeParameterDescriptorImpl.createForFurtherModification(
122 newOwner == null ? typeParameter.getContainingDeclaration() : newOwner,
123 typeParameter.getAnnotations(),
124 typeParameter.isReified(),
125 typeParameter.getVariance(),
126 typeParameter.getName(),
127 typeParameter.getIndex(),
128 SourceElement.NO_SOURCE
129 )
130 );
131 }
132 return result;
133 }
134
135 @NotNull
136 public static TypeSubstitutor createSubstitutorForTypeParameters(
137 @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters
138 ) {
139 Map<TypeConstructor, TypeProjection> typeSubstitutionContext = new HashMap<TypeConstructor, TypeProjection>();
140 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameter : originalToAltTypeParameters.entrySet()) {
141 typeSubstitutionContext.put(originalToAltTypeParameter.getKey().getTypeConstructor(),
142 new TypeProjectionImpl(originalToAltTypeParameter.getValue().getDefaultType()));
143 }
144 // TODO: Use IndexedParametersSubstitution here instead of map creation
145 return TypeSubstitutor.create(typeSubstitutionContext);
146 }
147 }