001/* 002 * Copyright (C) 2009 The Guava Authors 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 017package com.google.common.reflect; 018 019import static com.google.common.base.Preconditions.checkArgument; 020import static com.google.common.base.Preconditions.checkNotNull; 021import static com.google.common.base.Preconditions.checkState; 022 023import com.google.common.annotations.Beta; 024import com.google.common.base.Joiner; 025import com.google.common.collect.ImmutableMap; 026import com.google.common.collect.Maps; 027 028import java.lang.reflect.GenericArrayType; 029import java.lang.reflect.ParameterizedType; 030import java.lang.reflect.Type; 031import java.lang.reflect.TypeVariable; 032import java.lang.reflect.WildcardType; 033import java.util.Map; 034import java.util.concurrent.atomic.AtomicInteger; 035 036import javax.annotation.Nullable; 037 038/** 039 * An object of this class encapsulates type mappings from type variables. Mappings are established 040 * with {@link #where} and types are resolved using {@link #resolveType}. 041 * 042 * <p>Note that usually type mappings are already implied by the static type hierarchy (for example, 043 * the {@code E} type variable declared by class {@code List} naturally maps to {@code String} in 044 * the context of {@code class MyStringList implements List<String>}. In such case, prefer to use 045 * {@link TypeToken#resolveType} since it's simpler and more type safe. This class should only be 046 * used when the type mapping isn't implied by the static type hierarchy, but provided through other 047 * means such as an annotation or external configuration file. 048 * 049 * @author Ben Yu 050 * @since 15.0 051 */ 052@Beta 053public final class TypeResolver { 054 055 private final TypeTable typeTable; 056 057 public TypeResolver() { 058 this.typeTable = new TypeTable(); 059 } 060 061 private TypeResolver(TypeTable typeTable) { 062 this.typeTable = typeTable; 063 } 064 065 static TypeResolver accordingTo(Type type) { 066 return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(type)); 067 } 068 069 /** 070 * Returns a new {@code TypeResolver} with type variables in {@code formal} mapping to types in 071 * {@code actual}. 072 * 073 * <p>For example, if {@code formal} is a {@code TypeVariable T}, and {@code actual} is {@code 074 * String.class}, then {@code new TypeResolver().where(formal, actual)} will {@linkplain 075 * #resolveType resolve} {@code ParameterizedType List<T>} to {@code List<String>}, and resolve 076 * {@code Map<T, Something>} to {@code Map<String, Something>} etc. Similarly, {@code formal} and 077 * {@code actual} can be {@code Map<K, V>} and {@code Map<String, Integer>} respectively, or they 078 * can be {@code E[]} and {@code String[]} respectively, or even any arbitrary combination 079 * thereof. 080 * 081 * @param formal The type whose type variables or itself is mapped to other type(s). It's almost 082 * always a bug if {@code formal} isn't a type variable and contains no type variable. Make 083 * sure you are passing the two parameters in the right order. 084 * @param actual The type that the formal type variable(s) are mapped to. It can be or contain yet 085 * other type variables, in which case these type variables will be further resolved if 086 * corresponding mappings exist in the current {@code TypeResolver} instance. 087 */ 088 public TypeResolver where(Type formal, Type actual) { 089 Map<TypeVariable<?>, Type> mappings = Maps.newHashMap(); 090 populateTypeMappings(mappings, checkNotNull(formal), checkNotNull(actual)); 091 return where(mappings); 092 } 093 094 /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */ 095 TypeResolver where(Map<? extends TypeVariable<?>, ? extends Type> mappings) { 096 return new TypeResolver(typeTable.where(mappings)); 097 } 098 099 private static void populateTypeMappings( 100 final Map<TypeVariable<?>, Type> mappings, Type from, final Type to) { 101 if (from.equals(to)) { 102 return; 103 } 104 new TypeVisitor() { 105 @Override void visitTypeVariable(TypeVariable<?> typeVariable) { 106 mappings.put(typeVariable, to); 107 } 108 @Override void visitWildcardType(WildcardType fromWildcardType) { 109 WildcardType toWildcardType = expectArgument(WildcardType.class, to); 110 Type[] fromUpperBounds = fromWildcardType.getUpperBounds(); 111 Type[] toUpperBounds = toWildcardType.getUpperBounds(); 112 Type[] fromLowerBounds = fromWildcardType.getLowerBounds(); 113 Type[] toLowerBounds = toWildcardType.getLowerBounds(); 114 checkArgument( 115 fromUpperBounds.length == toUpperBounds.length 116 && fromLowerBounds.length == toLowerBounds.length, 117 "Incompatible type: %s vs. %s", fromWildcardType, to); 118 for (int i = 0; i < fromUpperBounds.length; i++) { 119 populateTypeMappings(mappings, fromUpperBounds[i], toUpperBounds[i]); 120 } 121 for (int i = 0; i < fromLowerBounds.length; i++) { 122 populateTypeMappings(mappings, fromLowerBounds[i], toLowerBounds[i]); 123 } 124 } 125 @Override void visitParameterizedType(ParameterizedType fromParameterizedType) { 126 ParameterizedType toParameterizedType = expectArgument(ParameterizedType.class, to); 127 checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()), 128 "Inconsistent raw type: %s vs. %s", fromParameterizedType, to); 129 Type[] fromArgs = fromParameterizedType.getActualTypeArguments(); 130 Type[] toArgs = toParameterizedType.getActualTypeArguments(); 131 checkArgument(fromArgs.length == toArgs.length, 132 "%s not compatible with %s", fromParameterizedType, toParameterizedType); 133 for (int i = 0; i < fromArgs.length; i++) { 134 populateTypeMappings(mappings, fromArgs[i], toArgs[i]); 135 } 136 } 137 @Override void visitGenericArrayType(GenericArrayType fromArrayType) { 138 Type componentType = Types.getComponentType(to); 139 checkArgument(componentType != null, "%s is not an array type.", to); 140 populateTypeMappings(mappings, fromArrayType.getGenericComponentType(), componentType); 141 } 142 @Override void visitClass(Class<?> fromClass) { 143 // Can't map from a raw class to anything other than itself. 144 // You can't say "assuming String is Integer". 145 // And we don't support "assuming String is T"; user has to say "assuming T is String". 146 throw new IllegalArgumentException("No type mapping from " + fromClass); 147 } 148 }.visit(from); 149 } 150 151 /** 152 * Resolves all type variables in {@code type} and all downstream types and 153 * returns a corresponding type with type variables resolved. 154 */ 155 public Type resolveType(Type type) { 156 checkNotNull(type); 157 if (type instanceof TypeVariable) { 158 return typeTable.resolve((TypeVariable<?>) type); 159 } else if (type instanceof ParameterizedType) { 160 return resolveParameterizedType((ParameterizedType) type); 161 } else if (type instanceof GenericArrayType) { 162 return resolveGenericArrayType((GenericArrayType) type); 163 } else if (type instanceof WildcardType) { 164 WildcardType wildcardType = (WildcardType) type; 165 return new Types.WildcardTypeImpl( 166 resolveTypes(wildcardType.getLowerBounds()), 167 resolveTypes(wildcardType.getUpperBounds())); 168 } else { 169 // if Class<?>, no resolution needed, we are done. 170 return type; 171 } 172 } 173 174 private Type[] resolveTypes(Type[] types) { 175 Type[] result = new Type[types.length]; 176 for (int i = 0; i < types.length; i++) { 177 result[i] = resolveType(types[i]); 178 } 179 return result; 180 } 181 182 private Type resolveGenericArrayType(GenericArrayType type) { 183 Type componentType = resolveType(type.getGenericComponentType()); 184 return Types.newArrayType(componentType); 185 } 186 187 private ParameterizedType resolveParameterizedType(ParameterizedType type) { 188 Type owner = type.getOwnerType(); 189 Type resolvedOwner = (owner == null) ? null : resolveType(owner); 190 Type resolvedRawType = resolveType(type.getRawType()); 191 192 Type[] vars = type.getActualTypeArguments(); 193 Type[] resolvedArgs = new Type[vars.length]; 194 for (int i = 0; i < vars.length; i++) { 195 resolvedArgs[i] = resolveType(vars[i]); 196 } 197 return Types.newParameterizedTypeWithOwner( 198 resolvedOwner, (Class<?>) resolvedRawType, resolvedArgs); 199 } 200 201 private static <T> T expectArgument(Class<T> type, Object arg) { 202 try { 203 return type.cast(arg); 204 } catch (ClassCastException e) { 205 throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName()); 206 } 207 } 208 209 /** A TypeTable maintains mapping from {@link TypeVariable} to types. */ 210 private static class TypeTable { 211 private final ImmutableMap<TypeVariable<?>, Type> map; 212 213 TypeTable() { 214 this.map = ImmutableMap.of(); 215 } 216 217 private TypeTable(ImmutableMap<TypeVariable<?>, Type> map) { 218 this.map = map; 219 } 220 221 /** Returns a new {@code TypeResolver} with {@code variable} mapping to {@code type}. */ 222 final TypeTable where(Map<? extends TypeVariable<?>, ? extends Type> mappings) { 223 ImmutableMap.Builder<TypeVariable<?>, Type> builder = ImmutableMap.builder(); 224 builder.putAll(map); 225 for (Map.Entry<? extends TypeVariable<?>, ? extends Type> mapping : mappings.entrySet()) { 226 TypeVariable<?> variable = mapping.getKey(); 227 Type type = mapping.getValue(); 228 checkArgument(!variable.equals(type), "Type variable %s bound to itself", variable); 229 builder.put(variable, type); 230 } 231 return new TypeTable(builder.build()); 232 } 233 234 final Type resolve(final TypeVariable<?> var) { 235 final TypeTable unguarded = this; 236 TypeTable guarded = new TypeTable() { 237 @Override public Type resolveInternal( 238 TypeVariable<?> intermediateVar, TypeTable forDependent) { 239 if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) { 240 return intermediateVar; 241 } 242 return unguarded.resolveInternal(intermediateVar, forDependent); 243 } 244 }; 245 return resolveInternal(var, guarded); 246 } 247 248 /** 249 * Resolves {@code var} using the encapsulated type mapping. If it maps to yet another 250 * non-reified type or has bounds, {@code forDependants} is used to do further resolution, which 251 * doesn't try to resolve any type variable on generic declarations that are already being 252 * resolved. 253 * 254 * <p>Should only be called and overridden by {@link #resolve(TypeVariable)}. 255 */ 256 Type resolveInternal(TypeVariable<?> var, TypeTable forDependants) { 257 Type type = map.get(var); 258 if (type == null) { 259 Type[] bounds = var.getBounds(); 260 if (bounds.length == 0) { 261 return var; 262 } 263 return Types.newTypeVariable( 264 var.getGenericDeclaration(), 265 var.getName(), 266 new TypeResolver(forDependants).resolveTypes(bounds)); 267 } 268 // in case the type is yet another type variable. 269 return new TypeResolver(forDependants).resolveType(type); 270 } 271 } 272 273 private static final class TypeMappingIntrospector extends TypeVisitor { 274 275 private static final WildcardCapturer wildcardCapturer = new WildcardCapturer(); 276 277 private final Map<TypeVariable<?>, Type> mappings = Maps.newHashMap(); 278 279 /** 280 * Returns type mappings using type parameters and type arguments found in 281 * the generic superclass and the super interfaces of {@code contextClass}. 282 */ 283 static ImmutableMap<TypeVariable<?>, Type> getTypeMappings( 284 Type contextType) { 285 TypeMappingIntrospector introspector = new TypeMappingIntrospector(); 286 introspector.visit(wildcardCapturer.capture(contextType)); 287 return ImmutableMap.copyOf(introspector.mappings); 288 } 289 290 @Override void visitClass(Class<?> clazz) { 291 visit(clazz.getGenericSuperclass()); 292 visit(clazz.getGenericInterfaces()); 293 } 294 295 @Override void visitParameterizedType(ParameterizedType parameterizedType) { 296 Class<?> rawClass = (Class<?>) parameterizedType.getRawType(); 297 TypeVariable<?>[] vars = rawClass.getTypeParameters(); 298 Type[] typeArgs = parameterizedType.getActualTypeArguments(); 299 checkState(vars.length == typeArgs.length); 300 for (int i = 0; i < vars.length; i++) { 301 map(vars[i], typeArgs[i]); 302 } 303 visit(rawClass); 304 visit(parameterizedType.getOwnerType()); 305 } 306 307 @Override void visitTypeVariable(TypeVariable<?> t) { 308 visit(t.getBounds()); 309 } 310 311 @Override void visitWildcardType(WildcardType t) { 312 visit(t.getUpperBounds()); 313 } 314 315 private void map(final TypeVariable<?> var, final Type arg) { 316 if (mappings.containsKey(var)) { 317 // Mapping already established 318 // This is possible when following both superClass -> enclosingClass 319 // and enclosingclass -> superClass paths. 320 // Since we follow the path of superclass first, enclosing second, 321 // superclass mapping should take precedence. 322 return; 323 } 324 // First, check whether var -> arg forms a cycle 325 for (Type t = arg; t != null; t = mappings.get(t)) { 326 if (var.equals(t)) { 327 // cycle detected, remove the entire cycle from the mapping so that 328 // each type variable resolves deterministically to itself. 329 // Otherwise, a F -> T cycle will end up resolving both F and T 330 // nondeterministically to either F or T. 331 for (Type x = arg; x != null; x = mappings.remove(x)) {} 332 return; 333 } 334 } 335 mappings.put(var, arg); 336 } 337 } 338 339 // This is needed when resolving types against a context with wildcards 340 // For example: 341 // class Holder<T> { 342 // void set(T data) {...} 343 // } 344 // Holder<List<?>> should *not* resolve the set() method to set(List<?> data). 345 // Instead, it should create a capture of the wildcard so that set() rejects any List<T>. 346 private static final class WildcardCapturer { 347 348 private final AtomicInteger id = new AtomicInteger(); 349 350 Type capture(Type type) { 351 checkNotNull(type); 352 if (type instanceof Class) { 353 return type; 354 } 355 if (type instanceof TypeVariable) { 356 return type; 357 } 358 if (type instanceof GenericArrayType) { 359 GenericArrayType arrayType = (GenericArrayType) type; 360 return Types.newArrayType(capture(arrayType.getGenericComponentType())); 361 } 362 if (type instanceof ParameterizedType) { 363 ParameterizedType parameterizedType = (ParameterizedType) type; 364 return Types.newParameterizedTypeWithOwner( 365 captureNullable(parameterizedType.getOwnerType()), 366 (Class<?>) parameterizedType.getRawType(), 367 capture(parameterizedType.getActualTypeArguments())); 368 } 369 if (type instanceof WildcardType) { 370 WildcardType wildcardType = (WildcardType) type; 371 Type[] lowerBounds = wildcardType.getLowerBounds(); 372 if (lowerBounds.length == 0) { // ? extends something changes to capture-of 373 Type[] upperBounds = wildcardType.getUpperBounds(); 374 String name = "capture#" + id.incrementAndGet() + "-of ? extends " 375 + Joiner.on('&').join(upperBounds); 376 return Types.newTypeVariable( 377 WildcardCapturer.class, name, wildcardType.getUpperBounds()); 378 } else { 379 // TODO(benyu): handle ? super T somehow. 380 return type; 381 } 382 } 383 throw new AssertionError("must have been one of the known types"); 384 } 385 386 private Type captureNullable(@Nullable Type type) { 387 if (type == null) { 388 return null; 389 } 390 return capture(type); 391 } 392 393 private Type[] capture(Type[] types) { 394 Type[] result = new Type[types.length]; 395 for (int i = 0; i < types.length; i++) { 396 result[i] = capture(types[i]); 397 } 398 return result; 399 } 400 } 401}