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.load.java.structure.impl;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.load.java.structure.*;
022
023 import java.util.Collection;
024 import java.util.HashMap;
025 import java.util.List;
026 import java.util.Map;
027
028 public class JavaTypeSubstitutorImpl implements JavaTypeSubstitutor {
029 private final Map<JavaTypeParameter, JavaType> substitutionMap;
030
031 public JavaTypeSubstitutorImpl(@NotNull Map<JavaTypeParameter, JavaType> substitutionMap) {
032 this.substitutionMap = substitutionMap;
033 }
034
035 @NotNull
036 @Override
037 public JavaType substitute(@NotNull JavaType type) {
038 JavaType substitutedType = substituteInternal(type);
039 return substitutedType != null ? substitutedType : correctSubstitutionForRawType(type);
040 }
041
042 @NotNull
043 // In case of raw type we get substitution map like T -> null,
044 // in this case we should substitute upper bound of T or,
045 // if it does not exist, return java.lang.Object
046 private JavaType correctSubstitutionForRawType(@NotNull JavaType original) {
047 if (original instanceof JavaClassifierType) {
048 JavaClassifier classifier = ((JavaClassifierType) original).getClassifier();
049 if (classifier instanceof JavaTypeParameter) {
050 return rawTypeForTypeParameter((JavaTypeParameter) classifier);
051 }
052 }
053
054 return original;
055 }
056
057 @Nullable
058 private JavaType substituteInternal(@NotNull JavaType type) {
059 if (type instanceof JavaClassifierType) {
060 JavaClassifierType classifierType = (JavaClassifierType) type;
061 JavaClassifier classifier = classifierType.getClassifier();
062
063 if (classifier instanceof JavaTypeParameter) {
064 return substitute((JavaTypeParameter) classifier);
065 }
066 else if (classifier instanceof JavaClass) {
067 JavaClass javaClass = (JavaClass) classifier;
068 Map<JavaTypeParameter, JavaType> substMap = new HashMap<JavaTypeParameter, JavaType>();
069 processClass(javaClass, classifierType.getSubstitutor(), substMap);
070
071 return javaClass.createImmediateType(new JavaTypeSubstitutorImpl(substMap));
072 }
073
074 return type;
075 }
076 else if (type instanceof JavaPrimitiveType) {
077 return type;
078 }
079 else if (type instanceof JavaArrayType) {
080 JavaType componentType = ((JavaArrayType) type).getComponentType();
081 JavaType substitutedComponentType = substitute(componentType);
082 if (substitutedComponentType == componentType) return type;
083
084 return substitutedComponentType.createArrayType();
085 }
086 else if (type instanceof JavaWildcardType) {
087 return substituteWildcardType((JavaWildcardType) type);
088 }
089
090 return type;
091 }
092
093 private void processClass(@NotNull JavaClass javaClass, @NotNull JavaTypeSubstitutor substitutor, @NotNull Map<JavaTypeParameter, JavaType> substMap) {
094 List<JavaTypeParameter> typeParameters = javaClass.getTypeParameters();
095 for (JavaTypeParameter typeParameter : typeParameters) {
096 JavaType substitutedParam = substitutor.substitute(typeParameter);
097 if (substitutedParam == null) {
098 substMap.put(typeParameter, null);
099 }
100 else {
101 substMap.put(typeParameter, substituteInternal(substitutedParam));
102 }
103 }
104
105 if (javaClass.isStatic()) {
106 return;
107 }
108
109 JavaClass outerClass = javaClass.getOuterClass();
110 if (outerClass != null) {
111 processClass(outerClass, substitutor, substMap);
112 }
113 }
114
115 @Nullable
116 private JavaType substituteWildcardType(@NotNull JavaWildcardType wildcardType) {
117 JavaType bound = wildcardType.getBound();
118 if (bound == null) {
119 return wildcardType;
120 }
121
122 JavaType newBound = substituteInternal(bound);
123 if (newBound == null) {
124 // This can be in case of substitution wildcard to raw type
125 return null;
126 }
127
128 return rebound(wildcardType, newBound);
129 }
130
131 @NotNull
132 private static JavaWildcardType rebound(@NotNull JavaWildcardType type, @NotNull JavaType newBound) {
133 if (type.getTypeProvider().createJavaLangObjectType().equals(newBound)) {
134 return type.getTypeProvider().createUnboundedWildcard();
135 }
136
137 if (type.isExtends()) {
138 return type.getTypeProvider().createUpperBoundWildcard(newBound);
139 }
140 else {
141 return type.getTypeProvider().createLowerBoundWildcard(newBound);
142 }
143 }
144
145 @NotNull
146 private JavaType rawTypeForTypeParameter(@NotNull JavaTypeParameter typeParameter) {
147 Collection<JavaClassifierType> bounds = typeParameter.getUpperBounds();
148 if (!bounds.isEmpty()) {
149 return substitute(bounds.iterator().next());
150 }
151
152 return typeParameter.getTypeProvider().createJavaLangObjectType();
153 }
154
155 @Override
156 @Nullable
157 public JavaType substitute(@NotNull JavaTypeParameter typeParameter) {
158 if (substitutionMap.containsKey(typeParameter)) {
159 return substitutionMap.get(typeParameter);
160 }
161
162 return typeParameter.getType();
163 }
164
165 @Override
166 @NotNull
167 public Map<JavaTypeParameter, JavaType> getSubstitutionMap() {
168 return substitutionMap;
169 }
170
171 @Override
172 public int hashCode() {
173 return substitutionMap.hashCode();
174 }
175
176 @Override
177 public boolean equals(Object obj) {
178 return obj instanceof JavaTypeSubstitutorImpl && substitutionMap.equals(((JavaTypeSubstitutorImpl) obj).substitutionMap);
179 }
180
181 @Override
182 public String toString() {
183 return getClass().getSimpleName() + ": " + substitutionMap;
184 }
185 }