Home | History | Annotate | Download | only in options
      1 // Copyright 2014 The Bazel Authors. All rights reserved.
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //    http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 package com.google.devtools.common.options;
     15 
     16 import com.google.common.annotations.VisibleForTesting;
     17 import com.google.common.primitives.Primitives;
     18 import com.google.common.reflect.TypeToken;
     19 
     20 import java.lang.reflect.Method;
     21 import java.lang.reflect.ParameterizedType;
     22 import java.lang.reflect.Type;
     23 import java.lang.reflect.TypeVariable;
     24 
     25 /**
     26  * A helper class for {@link OptionsParserImpl} to help checking the return type
     27  * of a {@link Converter} against the type of a field or the element type of a
     28  * list.
     29  *
     30  * <p>This class has to go through considerable contortion to get the correct result
     31  * from the Java reflection system, unfortunately. If the generic reflection part
     32  * had been better designed, some of this would not be necessary.
     33  */
     34 class GenericTypeHelper {
     35 
     36   /**
     37    * Returns the raw type of t, if t is either a raw or parameterized type.
     38    * Otherwise, this method throws an {@link AssertionError}.
     39    */
     40   @VisibleForTesting
     41   static Class<?> getRawType(Type t) {
     42     if (t instanceof Class<?>) {
     43       return (Class<?>) t;
     44     } else if (t instanceof ParameterizedType) {
     45       return (Class<?>) ((ParameterizedType) t).getRawType();
     46     } else {
     47       throw new AssertionError("A known concrete type is not concrete");
     48     }
     49   }
     50 
     51   /**
     52    * If type is a parameterized type, searches the given type variable in the list
     53    * of declared type variables, and then returns the corresponding actual type.
     54    * Returns null if the type variable is not defined by type.
     55    */
     56   private static Type matchTypeVariable(Type type, TypeVariable<?> variable) {
     57     if (type instanceof ParameterizedType) {
     58       Class<?> rawInterfaceType = getRawType(type);
     59       TypeVariable<?>[] typeParameters = rawInterfaceType.getTypeParameters();
     60       for (int i = 0; i < typeParameters.length; i++) {
     61         if (variable.equals(typeParameters[i])) {
     62           return ((ParameterizedType) type).getActualTypeArguments()[i];
     63         }
     64       }
     65     }
     66     return null;
     67   }
     68 
     69   /**
     70    * Resolves the return type of a method, in particular if the generic return
     71    * type ({@link Method#getGenericReturnType()}) is a type variable
     72    * ({@link TypeVariable}), by checking all super-classes and directly
     73    * implemented interfaces.
     74    *
     75    * <p>The method m must be defined by the given type or by its raw class type.
     76    *
     77    * @throws AssertionError if the generic return type could not be resolved
     78    */
     79   // TODO(bazel-team): also check enclosing classes and indirectly implemented
     80   // interfaces, which can also contribute type variables. This doesn't happen
     81   // in the existing use cases.
     82   public static Type getActualReturnType(Type type, Method method) {
     83     Type returnType = method.getGenericReturnType();
     84     if (returnType instanceof Class<?>) {
     85       return returnType;
     86     } else if (returnType instanceof ParameterizedType) {
     87       return returnType;
     88     } else if (returnType instanceof TypeVariable<?>) {
     89       TypeVariable<?> variable = (TypeVariable<?>) returnType;
     90       while (type != null) {
     91         Type candidate = matchTypeVariable(type, variable);
     92         if (candidate != null) {
     93           return candidate;
     94         }
     95 
     96         Class<?> rawType = getRawType(type);
     97         for (Type interfaceType : rawType.getGenericInterfaces()) {
     98           candidate = matchTypeVariable(interfaceType, variable);
     99           if (candidate != null) {
    100             return candidate;
    101           }
    102         }
    103 
    104         type = rawType.getGenericSuperclass();
    105       }
    106     }
    107     throw new AssertionError("The type " + returnType
    108         + " is not a Class, ParameterizedType, or TypeVariable");
    109   }
    110 
    111   /**
    112    * Determines if a value of a particular type (from) is assignable to a field of
    113    * a particular type (to). Also allows assigning wrapper types to primitive
    114    * types.
    115    *
    116    * <p>The checks done here should be identical to the checks done by
    117    * {@link java.lang.reflect.Field#set}. I.e., if this method returns true, a
    118    * subsequent call to {@link java.lang.reflect.Field#set} should succeed.
    119    */
    120   public static boolean isAssignableFrom(Type to, Type from) {
    121     if (to instanceof Class<?>) {
    122       Class<?> toClass = (Class<?>) to;
    123       if (toClass.isPrimitive()) {
    124         return Primitives.wrap(toClass).equals(from);
    125       }
    126     }
    127     return TypeToken.of(to).isSupertypeOf(from);
    128   }
    129 
    130   private GenericTypeHelper() {
    131     // Prevents Java from creating a public constructor.
    132   }
    133 }
    134