package com.fasterxml.jackson.databind.type;
import java.lang.reflect.*;
import java.util.*;
import com.fasterxml.jackson.databind.
JavaType;
/**
* Helper class used for resolving type parameters for given class
*/
public class
TypeBindings
{
private final static
JavaType[]
NO_TYPES = new
JavaType[0];
/**
* Marker to use for (temporarily) unbound references.
*/
public final static
JavaType UNBOUND = new
SimpleType(
Object.class);
/**
* Factory to use for constructing resolved related types.
*/
protected final
TypeFactory _typeFactory;
/**
* Context type used for resolving all types, if specified. May be null,
* in which case {@link #_contextClass} is used instead.
*/
protected final
JavaType _contextType;
/**
* Specific class to use for resolving all types, for methods and fields
* class and its superclasses and -interfaces contain.
*/
protected final
Class<?>
_contextClass;
/**
* Lazily-instantiated bindings of resolved type parameters
*/
protected
Map<
String,
JavaType>
_bindings;
/**
* Also: we may temporarily want to mark certain named types
* as resolved (but without exact type); if so, we'll just store
* names here.
*/
protected
HashSet<
String>
_placeholders;
/**
* Sometimes it is necessary to allow hierarchic resolution of types: specifically
* in cases where there are local bindings (for methods, constructors). If so,
* we'll just use simple delegation model.
*/
private final
TypeBindings _parentBindings;
/*
/**********************************************************
/* Construction
/**********************************************************
*/
public
TypeBindings(
TypeFactory typeFactory,
Class<?>
cc)
{
this(
typeFactory, null,
cc, null);
}
public
TypeBindings(
TypeFactory typeFactory,
JavaType type)
{
this(
typeFactory, null,
type.
getRawClass(),
type);
}
/**
* Constructor used to create "child" instances; mostly to
* allow delegation from explicitly defined local overrides
* (local type variables for methods, constructors) to
* contextual (class-defined) ones.
*/
public
TypeBindings childInstance() {
return new
TypeBindings(
_typeFactory, this,
_contextClass,
_contextType);
}
private
TypeBindings(
TypeFactory tf,
TypeBindings parent,
Class<?>
cc,
JavaType type)
{
_typeFactory =
tf;
_parentBindings =
parent;
_contextClass =
cc;
_contextType =
type;
}
/*
/**********************************************************
/* Pass-through type resolution methods
/**********************************************************
*/
public
JavaType resolveType(
Class<?>
cls) {
return
_typeFactory.
_constructType(
cls, this);
}
public
JavaType resolveType(
Type type) {
return
_typeFactory.
_constructType(
type, this);
}
/*
/**********************************************************
/* Accesors
/**********************************************************
*/
public int
getBindingCount() {
if (
_bindings == null) {
_resolve();
}
return
_bindings.
size();
}
public
JavaType findType(
String name)
{
if (
_bindings == null) {
_resolve();
}
JavaType t =
_bindings.
get(
name);
if (
t != null) {
return
t;
}
if (
_placeholders != null &&
_placeholders.
contains(
name)) {
return
UNBOUND;
}
if (
_parentBindings != null) {
return
_parentBindings.
findType(
name);
}
// nothing found, so...
// Should we throw an exception or just return null?
/* [JACKSON-499] 18-Feb-2011, tatu: There are some tricky type bindings within
* java.util, such as HashMap$KeySet; so let's punt the problem
* (honestly not sure what to do -- they are unbound for good, I think)
*/
if (
_contextClass != null) {
Class<?>
enclosing =
_contextClass.
getEnclosingClass();
if (
enclosing != null) {
// [JACKSON-572]: Actually, let's skip this for all non-static inner classes
// (which will also cover 'java.util' type cases...
if (!
Modifier.
isStatic(
_contextClass.
getModifiers())) {
return
UNBOUND;
}
// ... so this piece of code should not be needed any more
/*
Package pkg = enclosing.getPackage();
if (pkg != null) {
// as per [JACKSON-533], also include "java.util.concurrent":
if (pkg.getName().startsWith("java.util")) {
return UNBOUND;
}
}
*/
}
}
String className;
if (
_contextClass != null) {
className =
_contextClass.
getName();
} else if (
_contextType != null) {
className =
_contextType.
toString();
} else {
className = "UNKNOWN";
}
throw new
IllegalArgumentException("Type variable '"+
name
+"' can not be resolved (with context of class "+
className+")");
//t = UNBOUND;
}
public void addBinding(
String name,
JavaType type)
{
// note: emptyMap() is unmodifiable, hence second check is needed:
if (
_bindings == null ||
_bindings.
size() == 0) {
_bindings = new
LinkedHashMap<
String,
JavaType>();
}
_bindings.
put(
name,
type);
}
public
JavaType[] typesAsArray()
{
if (
_bindings == null) {
_resolve();
}
if (
_bindings.
size() == 0) {
return
NO_TYPES;
}
return
_bindings.
values().
toArray(new
JavaType[
_bindings.
size()]);
}
/*
/**********************************************************
/* Internal methods
/**********************************************************
*/
protected void
_resolve()
{
_resolveBindings(
_contextClass);
// finally: may have root level type info too
if (
_contextType != null) {
int
count =
_contextType.
containedTypeCount();
if (
count > 0) {
for (int
i = 0;
i <
count; ++
i) {
String name =
_contextType.
containedTypeName(
i);
JavaType type =
_contextType.
containedType(
i);
addBinding(
name,
type);
}
}
}
// nothing bound? mark with empty map to prevent further calls
if (
_bindings == null) {
_bindings =
Collections.
emptyMap();
}
}
public void
_addPlaceholder(
String name) {
if (
_placeholders == null) {
_placeholders = new
HashSet<
String>();
}
_placeholders.
add(
name);
}
protected void
_resolveBindings(
Type t)
{
if (
t == null) return;
Class<?>
raw;
if (
t instanceof
ParameterizedType) {
ParameterizedType pt = (
ParameterizedType)
t;
Type[]
args =
pt.
getActualTypeArguments();
if (
args != null &&
args.length > 0) {
Class<?>
rawType = (
Class<?>)
pt.
getRawType();
TypeVariable<?>[]
vars =
rawType.
getTypeParameters();
if (
vars.length !=
args.length) {
throw new
IllegalArgumentException("Strange parametrized type (in class "+
rawType.
getName()+"): number of type arguments != number of type parameters ("+
args.length+" vs "+
vars.length+")");
}
for (int
i = 0,
len =
args.length;
i <
len; ++
i) {
TypeVariable<?>
var =
vars[
i];
String name =
var.
getName();
if (
_bindings == null) {
_bindings = new
LinkedHashMap<
String,
JavaType>();
} else {
/* 24-Mar-2010, tatu: Better ensure that we do not overwrite something
* collected earlier (since we descend towards super-classes):
*/
if (
_bindings.
containsKey(
name)) continue;
}
// first: add a placeholder to prevent infinite loops
_addPlaceholder(
name);
// then resolve type
_bindings.
put(
name,
_typeFactory.
_constructType(
args[
i], this));
}
}
raw = (
Class<?>)
pt.
getRawType();
} else if (
t instanceof
Class<?>) {
raw = (
Class<?>)
t;
/* [JACKSON-677]: If this is an inner class then the generics are defined on the
* enclosing class so we have to check there as well. We don't
* need to call getEnclosingClass since anonymous classes declare
* generics
*/
Class<?>
decl =
raw.
getDeclaringClass();
/* 08-Feb-2013, tatu: Except that if context is also super-class, we must
* skip it; context will be checked anyway, and we'd get StackOverflow if
* we went there.
*/
if (
decl != null && !
decl.
isAssignableFrom(
raw)) {
_resolveBindings(
raw.
getDeclaringClass());
}
/* 24-Mar-2010, tatu: Can not have true generics definitions, but can
* have lower bounds ("<T extends BeanBase>") in declaration itself
*/
TypeVariable<?>[]
vars =
raw.
getTypeParameters();
if (
vars != null &&
vars.length > 0) {
JavaType[]
typeParams = null;
if (
_contextType != null &&
raw.
isAssignableFrom(
_contextType.
getRawClass())) {
typeParams =
_typeFactory.
findTypeParameters(
_contextType,
raw);
}
for (int
i = 0;
i <
vars.length;
i++) {
TypeVariable<?>
var =
vars[
i];
String name =
var.
getName();
Type varType =
var.
getBounds()[0];
if (
varType != null) {
if (
_bindings == null) {
_bindings = new
LinkedHashMap<
String,
JavaType>();
} else { // and no overwriting...
if (
_bindings.
containsKey(
name)) continue;
}
_addPlaceholder(
name); // to prevent infinite loops
if (
typeParams != null) {
_bindings.
put(
name,
typeParams[
i]);
} else {
_bindings.
put(
name,
_typeFactory.
_constructType(
varType, this));
}
}
}
}
} else { // probably can't be any of these... so let's skip for now
//if (type instanceof GenericArrayType) {
//if (type instanceof TypeVariable<?>) {
// if (type instanceof WildcardType) {
return;
}
// but even if it's not a parameterized type, its super types may be:
_resolveBindings(
raw.
getGenericSuperclass());
for (
Type intType :
raw.
getGenericInterfaces()) {
_resolveBindings(
intType);
}
}
@
Override
public
String toString()
{
if (
_bindings == null) {
_resolve();
}
StringBuilder sb = new
StringBuilder("[TypeBindings for ");
if (
_contextType != null) {
sb.
append(
_contextType.
toString());
} else {
sb.
append(
_contextClass.
getName());
}
sb.
append(": ").
append(
_bindings).
append("]");
return
sb.
toString();
}
}