/* * Copyright 2015 MovingBlocks * * 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 org.terasology.util.reflection; import com.googlecode.gentyref.GenericTypeReflector; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Optional; /** * Utility methods for working with Generics in reflection. * * @author Immortius */ public final class GenericsUtil { private GenericsUtil() { } /** * Used to obtain the bound value for a generic parameter of a type. Example, for a field of type List<String>, the 0th generic parameter is String.class. * A List with no parameter will return Optional.absent() * * @param target The type to obtain the generic parameter of. * @param index The index of the the parameter to obtain * @return An optional that contains the parameter type if bound. */ public static Optional<Type> getTypeParameterBinding(Type target, int index) { return getTypeParameterBindingForInheritedClass(target, getClassOfType(target), index); } /** * Used to obtained the bound value for a generic parameter of a particular class or interface that the type inherits. * * @param target The type to obtain the generic parameter of. * @param superClass The superclass which the parameter belongs to * @param index The index of the parameter to obtain * @param <T> The type of the superclass that the parameter belongs to * @return An optional that contains the parameter if bound. */ public static <T> Optional<Type> getTypeParameterBindingForInheritedClass(Type target, Class<T> superClass, int index) { if (superClass.getTypeParameters().length == 0) { throw new IllegalArgumentException("Class '" + superClass + "' is not parameterized"); } if (!superClass.isAssignableFrom(getClassOfType(target))) { throw new IllegalArgumentException("Class '" + target + "' does not implement '" + superClass + "'"); } Type type = GenericTypeReflector.getExactSuperType(target, superClass); if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type paramType = parameterizedType.getActualTypeArguments()[index]; if (paramType instanceof Class || paramType instanceof ParameterizedType) { return Optional.of(paramType); } } return Optional.empty(); } /** * Returns the raw class of a type, or null if the type doesn't represent a class. * * @param type The type to get the class of * @return the raw class of a type, or null if the type doesn't represent a class. */ public static Class<?> getClassOfType(Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { return (Class) ((ParameterizedType) type).getRawType(); } return null; } }