Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright (C) 2009 The Guava Authors
   *
   * 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 com.google.common.reflect;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 
 
 import java.util.Map;
 
 import  javax.annotation.Nullable;

An object of this class encapsulates type mappings from type variables. Mappings are established with where and types are resolved using resolveType.

Note that usually type mappings are already implied by the static type hierarchy (for example, the E type variable declared by class List naturally maps to String in the context of class MyStringList implements List<String>. In such case, prefer to use TypeToken.resolveType since it's simpler and more type safe. This class should only be used when the type mapping isn't implied by the static type hierarchy, but provided through other means such as an annotation or external configuration file.

Author(s):
Ben Yu
Since:
15.0
 
 public final class TypeResolver {
 
   private final TypeTable typeTable;
 
   public TypeResolver() {
     this. = new TypeTable();
   }
 
   private TypeResolver(TypeTable typeTable) {
     this. = typeTable;
   }
 
   static TypeResolver accordingTo(Type type) {
     return new TypeResolver().where(TypeMappingIntrospector.getTypeMappings(type));
   }

  
Returns a new TypeResolver with type variables in formal mapping to types in actual.

For example, if formal is a TypeVariable T, and actual is String.class, then new TypeResolver().where(formal, actual) will resolveType resolve ParameterizedType List<T> to List<String>, and resolve Map<T, Something> to Map<String, Something> etc. Similarly, formal and actual can be Map<K, V> and Map<String, Integer> respectively, or they can be E[] and String[] respectively, or even any arbitrary combination thereof.

Parameters:
formal The type whose type variables or itself is mapped to other type(s). It's almost always a bug if formal isn't a type variable and contains no type variable. Make sure you are passing the two parameters in the right order.
actual The type that the formal type variable(s) are mapped to. It can be or contain yet other type variables, in which case these type variables will be further resolved if corresponding mappings exist in the current TypeResolver instance.
 
   public TypeResolver where(Type formalType actual) {
     Map<TypeVariable<?>, Typemappings = Maps.newHashMap();
     populateTypeMappings(mappingscheckNotNull(formal), checkNotNull(actual));
     return where(mappings);
   }

  
Returns a new TypeResolver with variable mapping to type.
 
   TypeResolver where(Map<? extends TypeVariable<?>, ? extends Typemappings) {
     return new TypeResolver(.where(mappings));
   }
 
   private static void populateTypeMappings(
      final Map<TypeVariable<?>, TypemappingsType fromfinal Type to) {
    if (from.equals(to)) {
      return;
    }
    new TypeVisitor() {
      @Override void visitTypeVariable(TypeVariable<?> typeVariable) {
        mappings.put(typeVariableto);
      }
      @Override void visitWildcardType(WildcardType fromWildcardType) {
        WildcardType toWildcardType = expectArgument(WildcardType.classto);
        Type[] fromUpperBounds = fromWildcardType.getUpperBounds();
        Type[] toUpperBounds = toWildcardType.getUpperBounds();
        Type[] fromLowerBounds = fromWildcardType.getLowerBounds();
        Type[] toLowerBounds = toWildcardType.getLowerBounds();
        checkArgument(
            fromUpperBounds.length == toUpperBounds.length
                && fromLowerBounds.length == toLowerBounds.length,
            "Incompatible type: %s vs. %s"fromWildcardTypeto);
        for (int i = 0; i < fromUpperBounds.lengthi++) {
          populateTypeMappings(mappingsfromUpperBounds[i], toUpperBounds[i]);
        }
        for (int i = 0; i < fromLowerBounds.lengthi++) {
          populateTypeMappings(mappingsfromLowerBounds[i], toLowerBounds[i]);
        }
      }
      @Override void visitParameterizedType(ParameterizedType fromParameterizedType) {
        ParameterizedType toParameterizedType = expectArgument(ParameterizedType.classto);
        checkArgument(fromParameterizedType.getRawType().equals(toParameterizedType.getRawType()),
            "Inconsistent raw type: %s vs. %s"fromParameterizedTypeto);
        Type[] fromArgs = fromParameterizedType.getActualTypeArguments();
        Type[] toArgs = toParameterizedType.getActualTypeArguments();
        checkArgument(fromArgs.length == toArgs.length,
            "%s not compatible with %s"fromParameterizedTypetoParameterizedType);
        for (int i = 0; i < fromArgs.lengthi++) {
          populateTypeMappings(mappingsfromArgs[i], toArgs[i]);
        }
      }
      @Override void visitGenericArrayType(GenericArrayType fromArrayType) {
        Type componentType = Types.getComponentType(to);
        checkArgument(componentType != null"%s is not an array type."to);
        populateTypeMappings(mappingsfromArrayType.getGenericComponentType(), componentType);
      }
      @Override void visitClass(Class<?> fromClass) {
        // Can't map from a raw class to anything other than itself.
        // You can't say "assuming String is Integer".
        // And we don't support "assuming String is T"; user has to say "assuming T is String". 
        throw new IllegalArgumentException("No type mapping from " + fromClass);
      }
    }.visit(from);
  }

  
Resolves all type variables in type and all downstream types and returns a corresponding type with type variables resolved.
  public Type resolveType(Type type) {
    checkNotNull(type);
    if (type instanceof TypeVariable) {
      return .resolve((TypeVariable<?>) type);
    } else if (type instanceof ParameterizedType) {
      return resolveParameterizedType((ParameterizedTypetype);
    } else if (type instanceof GenericArrayType) {
      return resolveGenericArrayType((GenericArrayTypetype);
    } else if (type instanceof WildcardType) {
      WildcardType wildcardType = (WildcardTypetype;
      return new Types.WildcardTypeImpl(
          resolveTypes(wildcardType.getLowerBounds()),
          resolveTypes(wildcardType.getUpperBounds()));
    } else {
      // if Class<?>, no resolution needed, we are done.
      return type;
    }
  }
  private Type[] resolveTypes(Type[] types) {
    Type[] result = new Type[types.length];
    for (int i = 0; i < types.lengthi++) {
      result[i] = resolveType(types[i]);
    }
    return result;
  }
    Type componentType = resolveType(type.getGenericComponentType());
    return Types.newArrayType(componentType);
  }
    Type owner = type.getOwnerType();
    Type resolvedOwner = (owner == null) ? null : resolveType(owner);
    Type resolvedRawType = resolveType(type.getRawType());
    Type[] vars = type.getActualTypeArguments();
    Type[] resolvedArgs = new Type[vars.length];
    for (int i = 0; i < vars.lengthi++) {
      resolvedArgs[i] = resolveType(vars[i]);
    }
    return Types.newParameterizedTypeWithOwner(
        resolvedOwner, (Class<?>) resolvedRawTyperesolvedArgs);
  }
  private static <T> T expectArgument(Class<T> typeObject arg) {
    try {
      return type.cast(arg);
    } catch (ClassCastException e) {
      throw new IllegalArgumentException(arg + " is not a " + type.getSimpleName());
    }
  }

  
A TypeTable maintains mapping from TypeVariable to types.
  private static class TypeTable {
    private final ImmutableMap<TypeVariable<?>, Typemap;
  
    TypeTable() {
      this. = ImmutableMap.of();
    }
    
    private TypeTable(ImmutableMap<TypeVariable<?>, Typemap) {
      this. = map;
    }

    
Returns a new TypeResolver with variable mapping to type.
    final TypeTable where(Map<? extends TypeVariable<?>, ? extends Typemappings) {
      ImmutableMap.Builder<TypeVariable<?>, Typebuilder = ImmutableMap.builder();
      builder.putAll();
      for (Map.Entry<? extends TypeVariable<?>, ? extends Typemapping : mappings.entrySet()) {
        TypeVariable<?> variable = mapping.getKey();
        Type type = mapping.getValue();
        checkArgument(!variable.equals(type), "Type variable %s bound to itself"variable);
        builder.put(variabletype);
      }
      return new TypeTable(builder.build());
    }
    final Type resolve(final TypeVariable<?> var) {
      final TypeTable unguarded = this;
      TypeTable guarded = new TypeTable() {
        @Override public Type resolveInternal(
            TypeVariable<?> intermediateVarTypeTable forDependent) {
          if (intermediateVar.getGenericDeclaration().equals(var.getGenericDeclaration())) {
            return intermediateVar;
          }
          return unguarded.resolveInternal(intermediateVarforDependent);
        }
      };
      return resolveInternal(varguarded);
    }

    
Resolves var using the encapsulated type mapping. If it maps to yet another non-reified type or has bounds, forDependants is used to do further resolution, which doesn't try to resolve any type variable on generic declarations that are already being resolved.

Should only be called and overridden by resolve(TypeVariable).

    Type resolveInternal(TypeVariable<?> varTypeTable forDependants) {
      Type type = .get(var);
      if (type == null) {
        Type[] bounds = var.getBounds();
        if (bounds.length == 0) {
          return var;
        }
        return Types.newTypeVariable(
            var.getGenericDeclaration(),
            var.getName(),
            new TypeResolver(forDependants).resolveTypes(bounds));
      }
      // in case the type is yet another type variable.
      return new TypeResolver(forDependants).resolveType(type);
    }
  }
  private static final class TypeMappingIntrospector extends TypeVisitor {
    private static final WildcardCapturer wildcardCapturer = new WildcardCapturer();
    private final Map<TypeVariable<?>, Typemappings = Maps.newHashMap();

    
Returns type mappings using type parameters and type arguments found in the generic superclass and the super interfaces of contextClass.
        Type contextType) {
      TypeMappingIntrospector introspector = new TypeMappingIntrospector();
      introspector.visit(.capture(contextType));
      return ImmutableMap.copyOf(introspector.mappings);
    }
    @Override void visitClass(Class<?> clazz) {
      visit(clazz.getGenericSuperclass());
      visit(clazz.getGenericInterfaces());
    }
    @Override void visitParameterizedType(ParameterizedType parameterizedType) {
      Class<?> rawClass = (Class<?>) parameterizedType.getRawType();
      TypeVariable<?>[] vars = rawClass.getTypeParameters();
      Type[] typeArgs = parameterizedType.getActualTypeArguments();
      checkState(vars.length == typeArgs.length);
      for (int i = 0; i < vars.lengthi++) {
        map(vars[i], typeArgs[i]);
      }
      visit(rawClass);
      visit(parameterizedType.getOwnerType());
    }
    @Override void visitTypeVariable(TypeVariable<?> t) {
      visit(t.getBounds());
    }
      visit(t.getUpperBounds());
    }
    private void map(final TypeVariable<?> varfinal Type arg) {
      if (.containsKey(var)) {
        // Mapping already established
        // This is possible when following both superClass -> enclosingClass
        // and enclosingclass -> superClass paths.
        // Since we follow the path of superclass first, enclosing second,
        // superclass mapping should take precedence.
        return;
      }
      // First, check whether var -> arg forms a cycle
      for (Type t = argt != nullt = .get(t)) {
        if (var.equals(t)) {
          // cycle detected, remove the entire cycle from the mapping so that
          // each type variable resolves deterministically to itself.
          // Otherwise, a F -> T cycle will end up resolving both F and T
          // nondeterministically to either F or T.
          for (Type x = argx != nullx = .remove(x)) {}
          return;
        }
      }
      .put(vararg);
    }
  }
  // This is needed when resolving types against a context with wildcards
  // For example:
  // class Holder<T> {
  //   void set(T data) {...}
  // }
  // Holder<List<?>> should *not* resolve the set() method to set(List<?> data).
  // Instead, it should create a capture of the wildcard so that set() rejects any List<T>.
  private static final class WildcardCapturer {
    private final AtomicInteger id = new AtomicInteger();
    Type capture(Type type) {
      checkNotNull(type);
      if (type instanceof Class) {
        return type;
      }
      if (type instanceof TypeVariable) {
        return type;
      }
      if (type instanceof GenericArrayType) {
        GenericArrayType arrayType = (GenericArrayTypetype;
        return Types.newArrayType(capture(arrayType.getGenericComponentType()));
      }
      if (type instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedTypetype;
        return Types.newParameterizedTypeWithOwner(
            captureNullable(parameterizedType.getOwnerType()),
            (Class<?>) parameterizedType.getRawType(),
            capture(parameterizedType.getActualTypeArguments()));
      }
      if (type instanceof WildcardType) {
        WildcardType wildcardType = (WildcardTypetype;
        Type[] lowerBounds = wildcardType.getLowerBounds();
        if (lowerBounds.length == 0) { // ? extends something changes to capture-of
          Type[] upperBounds = wildcardType.getUpperBounds();
          String name = "capture#" + .incrementAndGet() + "-of ? extends "
              + Joiner.on('&').join(upperBounds);
          return Types.newTypeVariable(
              WildcardCapturer.classnamewildcardType.getUpperBounds());
        } else {
          // TODO(benyu): handle ? super T somehow.
          return type;
        }
      }
      throw new AssertionError("must have been one of the known types");
    }
    private Type captureNullable(@Nullable Type type) {
      if (type == null) {
        return null;
      }
      return capture(type);
    }
    private Type[] capture(Type[] types) {
      Type[] result = new Type[types.length];
      for (int i = 0; i < types.lengthi++) {
        result[i] = capture(types[i]);
      }
      return result;
    }
  }
New to GrepCode? Check out our FAQ X