/*
* 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.jdbi.v3.core.generic;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Map;
import java.util.Optional;
import org.jdbi.v3.core.generic.internal.TypeParameter;
import org.jdbi.v3.core.generic.internal.TypeToken;
public class GenericTypes {
private static final TypeVariable<Class<Map>> KEY;
private static final TypeVariable<Class<Map>> VALUE;
static {
TypeVariable<Class<Map>>[] mapParams = Map.class.getTypeParameters();
KEY = mapParams[0];
VALUE = mapParams[1];
}
/**
* Returns the erased class for the given type.
*
* <p>
* Example: if type is <code>List<String></code>, returns
* <code>List.class</code>
* </p>
* parameter
*
* @param type
* the type
* @return the erased class
*/
public static Class<?> getErasedType(Type type) {
return TypeToken.of(type).getRawType();
}
/**
* For the given type which extends parameterizedSupertype, returns the
* first generic parameter for parameterized supertype, if concretely
* expressed.
*
* <p>
* Example:
* </p>
* <ul>
* <li>if <code>type</code> is <code>ArrayList<String></code> and
* <code>parameterizedSuperType</code> is <code>List.class</code>, returns
* <code>Optional.of(String.class)</code>.</li>
* <li>if <code>type</code> is <code>ArrayList.class</code> (raw) and
* <code>parameterizedSuperType</code> is <code>List.class</code>, returns
* <code>Optional.empty()</code>.</li>
* </ul>
*
* @param type
* the subtype of parameterizedSupertype
* @param parameterizedSupertype
* the parameterized supertype from which we want the generic
* parameter
* @return the parameter on the supertype, if it is concretely defined.
*/
public static Optional<Type> findGenericParameter(Type type, Class<?> parameterizedSupertype) {
Type parameterType = resolveType(parameterizedSupertype.getTypeParameters()[0], type);
return parameterType instanceof Class || parameterType instanceof ParameterizedType
? Optional.of(parameterType)
: Optional.empty();
}
/**
* Resolves the <code>type</code> parameter in the context of <code>contextType</code>. For example, if
* <code>type</code> is <code>List.class.getMethod("get", int.class).getGenericReturnType()</code>, and
* <code>contextType</code> is <code>List<String></code>, this method returns <code>String.class</code>
* @param type the type to be resolved in the scope of <code>contextType</code>
* @param contextType the context type in which <code>type</code> is interpreted to resolve the type.
* @return the resolved type.
*/
public static Type resolveType(Type type, Type contextType) {
return TypeToken.of(contextType)
.resolveType(type)
.getType();
}
/**
* @return whether a {@code Type} is an Array type.
*/
public static boolean isArray(Type type) {
return type instanceof Class<?> && ((Class<?>) type).isArray();
}
/**
* Given a subtype of {@code Map<K,V>}, returns the corresponding map entry type {@code Map.Entry<K,V>}.
* @param mapType the map subtype
* @return the map entry type
*/
public static Type resolveMapEntryType(Type mapType) {
Type keyType = resolveType(KEY, mapType);
Type valueType = resolveType(VALUE, mapType);
return resolveMapEntryType(keyType, valueType);
}
/**
* Given a key and value type, returns the map entry type {@code Map.Entry<keyType,valueType>}.
* @param keyType the key type
* @param valueType the value type
* @return the map entry type
*/
public static Type resolveMapEntryType(Type keyType, Type valueType) {
return resolveMapEntryType(TypeToken.of(keyType), TypeToken.of(valueType));
}
private static <K, V> Type resolveMapEntryType(TypeToken<K> keyType, TypeToken<V> valueType) {
return new TypeToken<Map.Entry<K, V>>() {}
.where(new TypeParameter<K>() {}, keyType)
.where(new TypeParameter<V>() {}, valueType)
.getType();
}
}