// =================================================================================================
// Copyright 2011 Twitter, Inc.
// -------------------------------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this work except in compliance with the License.
// You may obtain a copy of the License in the LICENSE file, or 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.twitter.common.args;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.List;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
/**
* Utility class to extract generic type information.
*
* TODO(William Farner): Move this into a common library, integrate with EasyMockTest.Clazz.
*
* @author William Farner
*/
public final class TypeUtil {
private static final Function<Type, Type> GET_TYPE = new Function<Type, Type>() {
@Override public Type apply(Type type) {
if (type instanceof WildcardType) {
return apply(((WildcardType) type).getUpperBounds()[0]);
}
return type;
}
};
private TypeUtil() {
// Utility.
}
/**
* Gets the types that a type is type-parameterized with, in declaration order.
*
* @param type The type to extract type parameters from.
* @return The types that {@code type} is parameterized with.
*/
public static List<Type> getTypeParams(Type type) {
if (type instanceof WildcardType) {
return getTypeParams(GET_TYPE.apply(type));
}
return Lists.transform(Arrays.asList(
((ParameterizedType) type).getActualTypeArguments()), GET_TYPE);
}
/**
* Finds the raw class of type.
*
* @param type The type to get the raw class of.
* @return The raw class of type.
*/
public static Class<?> getRawType(Type type) {
if (type instanceof ParameterizedType) {
return getRawType(((ParameterizedType) type).getRawType());
}
if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
}
return (Class<?>) type;
}
/**
* Convenience method to call {@link #getTypeParam(Field)}, with the requirement that there
* is exactly one type parameter on the field.
*
* @param field The field to extract type parameters from.
* @return The raw classes of types that {@code field} is parameterized with.
*/
public static TypeToken<?> getTypeParamTypeToken(Field field) {
List<Type> typeParams = getTypeParams(field.getGenericType());
Preconditions.checkArgument(typeParams.size() == 1,
"Expected exactly one type parameter for field " + field);
return TypeToken.of(typeParams.get(0));
}
/**
* Gets the type parameter from a field. Assumes that there is at least one type parameter.
*
* @param field The field to extract the type parameter from.
* @return The field type parameter.
*/
public static Type getTypeParam(Field field) {
return extractTypeToken(field.getGenericType());
}
/**
* Extracts the actual type parameter for a singly parameterized type.
*
* @param type The parameterized type to extract the type argument from.
* @return The type of the single specified type parameter for {@code type}.
* @throws IllegalArgumentException if the supplied type does not have exactly one specified type
* parameter
*/
public static Type extractTypeToken(Type type) {
Preconditions.checkNotNull(type);
Preconditions.checkArgument(type instanceof ParameterizedType, "Missing type parameter.");
Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
Preconditions.checkArgument(typeArguments.length == 1,
"Expected a type with exactly 1 type argument");
return typeArguments[0];
}
}