/*
* 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;
Utilities for working with generic types.
/**
* Utilities for working with generic types.
*/
@SuppressWarnings("rawtypes")
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];
}
private GenericTypes() {
throw new UnsupportedOperationException("utility class");
}
Returns the erased class for the given type.
Example: if type is List<String>
, returns
List.class
parameter
Params: - type –
the type
Returns: the erased class
/**
* 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.
Example:
- if
type
is ArrayList<String>
and
parameterizedSuperType
is List.class
, returns
Optional.of(String.class)
.
- if
type
is ArrayList.class
(raw) and
parameterizedSuperType
is List.class
, returns
Optional.empty()
.
Params: - type –
the subtype of parameterizedSupertype
- parameterizedSupertype –
the parameterized supertype from which we want the generic
parameter
Returns: the parameter on the supertype, if it is concretely defined.
/**
* 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 type
parameter in the context of contextType
. For example, if
type
is List.class.getMethod("get", int.class).getGenericReturnType()
, and
contextType
is List<String>
, this method returns String.class
Params: - type – the type to be resolved in the scope of
contextType
- contextType – the context type in which
type
is interpreted to resolve the type.
Returns: the resolved type.
/**
* 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();
}
Params: - type – a type
Returns: whether the given Type
is an Array type.
/**
* @param type a type
* @return whether the given {@code Type} is an Array type.
*/
public static boolean isArray(Type type) {
return type instanceof Class<?> && ((Class<?>) type).isArray();
}
Given a subtype of Map<K,V>
, returns the corresponding map entry type Map.Entry<K,V>
. Params: - mapType – the map subtype
Returns: the map entry type
/**
* 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 Map.Entry<keyType,valueType>
. Params: - keyType – the key type
- valueType – the value type
Returns: the map entry type
/**
* 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();
}
}