/*
* Copyright 2016 the original author or 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 org.springframework.cloud.stream.reflection;
import org.springframework.core.ResolvableType;
import org.springframework.util.Assert;
/**
* Internal utilities for handling generics.
*
* @author Marius Bogoevici
*/
public abstract class GenericsUtils {
/**
* For a specific class that implements or extends a parametrized type
* returns the parameter of that interface at a given position. For example,
* for this class:
* <pre>
* {@code
* class MessageChannelBinder implements Binder<MessageChannel, ?, ?>
* }
*
* <pre>
* {@code
* getParameterType(MessageChannelBinder.class, Binder.class, 0);
* }
*
* will return {@code Binder}
*
* @param evaluatedClass the evaluated class
* @param interfaceClass the parametrized interface
* @param position the position
* @return the parameter type if any
* @throws IllegalStateException if the evaluated class does not implement the interface or
*/
public static Class<?> getParameterType(Class<?> evaluatedClass, Class<?> interfaceClass, int position) {
Class<?> bindableType = null;
Assert.isTrue(interfaceClass.isInterface(), "'interfaceClass' must be an interface");
if (!interfaceClass.isAssignableFrom(evaluatedClass)) {
throw new IllegalStateException(evaluatedClass + " does not implement " + interfaceClass);
}
ResolvableType currentType = ResolvableType.forType(evaluatedClass);
while (!Object.class.equals(currentType.getRawClass()) && bindableType == null) {
ResolvableType[] interfaces = currentType.getInterfaces();
ResolvableType resolvableType = null;
for (ResolvableType interfaceType : interfaces) {
if (interfaceClass.equals(interfaceType.getRawClass())) {
resolvableType = interfaceType;
break;
}
}
if (resolvableType == null) {
currentType = currentType.getSuperType();
}
else {
ResolvableType[] generics = resolvableType.getGenerics();
ResolvableType generic = generics[position];
Class<?> resolvedParameter = generic.resolve();
if (resolvedParameter != null) {
bindableType = resolvedParameter;
}
else {
bindableType = Object.class;
}
}
}
if (bindableType == null) {
throw new IllegalStateException("Cannot find parameter of " + evaluatedClass.getName() + " for "
+ interfaceClass + " at position " + position);
}
return bindableType;
}
}