/* * Copyright (C) 2006 The Guava 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.jdbi.v3.core.generic.internal; import static java.util.Collections.singletonMap; import static org.jdbi.v3.core.generic.internal.Preconditions.checkNotNull; import static org.jdbi.v3.core.generic.internal.Preconditions.checkState; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; public class TypeToken<T> extends TypeCapture<T> { private final Type runtimeType; private transient TypeResolver typeResolver; protected TypeToken() { this.runtimeType = capture(); checkState( !(runtimeType instanceof TypeVariable), "Cannot construct a TypeToken for a type variable.\n" + "You probably meant to call new TypeToken<%s>(getClass()) " + "that can resolve the type variable for you.\n" + "If you do need to create a TypeToken of a type variable, " + "please use TypeToken.of() instead.", runtimeType); } private TypeToken(Type type) { this.runtimeType = checkNotNull(type, "type"); } public static TypeToken<?> of(Type type) { return new SimpleTypeToken<>(type); } private static final class SimpleTypeToken<T> extends TypeToken<T> { SimpleTypeToken(Type type) { super(type); } } public final <X> TypeToken<T> where(TypeParameter<X> typeParam, TypeToken<X> typeArg) { TypeResolver resolver =new TypeResolver() .where(singletonMap(new TypeResolver.TypeVariableKey(typeParam.typeVariable), typeArg.runtimeType)); // If there's any type error, we'd report now rather than later. return new SimpleTypeToken<T>(resolver.resolveType(runtimeType)); } public final TypeToken<?> resolveType(Type type) { checkNotNull(type, "type"); TypeResolver resolver = typeResolver; if (resolver == null) { resolver = (typeResolver = TypeResolver.accordingTo(runtimeType)); } return of(resolver.resolveType(type)); } public final Type getType() { return runtimeType; } public final Class<? super T> getRawType() { // For wildcard or type variable, the first bound determines the runtime type. Class<?> rawType = getRawTypes().iterator().next(); @SuppressWarnings("unchecked") // raw type is |T| Class<? super T> result = (Class<? super T>) rawType; return result; } private Set<Class<? super T>> getRawTypes() { final Set<Class<?>> builder = new LinkedHashSet<>(); new TypeVisitor() { @Override void visitTypeVariable(TypeVariable<?> t) { visit(t.getBounds()); } @Override void visitWildcardType(WildcardType t) { visit(t.getUpperBounds()); } @Override void visitParameterizedType(ParameterizedType t) { builder.add((Class<?>) t.getRawType()); } @Override void visitClass(Class<?> t) { builder.add(t); } @Override void visitGenericArrayType(GenericArrayType t) { builder.add(Types.getArrayClass(of(t.getGenericComponentType()).getRawType())); } }.visit(runtimeType); // Cast from ImmutableSet<Class<?>> to ImmutableSet<Class<? super T>> @SuppressWarnings({"unchecked", "rawtypes"}) Set<Class<? super T>> result = (Set) Collections.unmodifiableSet(builder); return result; } }