Skip to content

Commit 07d946c

Browse files
committed
Fixed #76 -- and also rewrote the whole type resolution system, which will require cascading changes in various datatype modules
1 parent 3699c3d commit 07d946c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2461
-1439
lines changed

attic/TypeBindings.java

+337
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
package com.fasterxml.jackson.databind.type;
2+
3+
import java.lang.reflect.*;
4+
import java.util.*;
5+
6+
import com.fasterxml.jackson.databind.JavaType;
7+
import com.fasterxml.jackson.databind.util.ClassUtil;
8+
9+
/**
10+
* Helper class used for resolving type parameters for given class
11+
*/
12+
public class TypeBindings
13+
{
14+
private final static JavaType[] NO_TYPES = new JavaType[0];
15+
16+
/**
17+
* Marker to use for (temporarily) unbound references.
18+
*/
19+
public final static JavaType UNBOUND = new SimpleType(Object.class);
20+
21+
/**
22+
* Factory to use for constructing resolved related types.
23+
*/
24+
protected final TypeFactory _typeFactory;
25+
26+
/**
27+
* @since 2.7
28+
*/
29+
protected final ClassStack _classStack;
30+
31+
/**
32+
* Context type used for resolving all types, if specified. May be null,
33+
* in which case {@link #_contextClass} is used instead.
34+
*/
35+
protected final JavaType _contextType;
36+
37+
/**
38+
* Specific class to use for resolving all types, for methods and fields
39+
* class and its superclasses and -interfaces contain.
40+
*/
41+
protected final Class<?> _contextClass;
42+
43+
/**
44+
* Lazily-instantiated bindings of resolved type parameters
45+
*/
46+
protected Map<String,JavaType> _bindings;
47+
48+
/**
49+
* Also: we may temporarily want to mark certain named types
50+
* as resolved (but without exact type); if so, we'll just store
51+
* names here.
52+
*/
53+
protected HashSet<String> _placeholders;
54+
55+
/**
56+
* Sometimes it is necessary to allow hierarchic resolution of types: specifically
57+
* in cases where there are local bindings (for methods, constructors). If so,
58+
* we'll just use simple delegation model.
59+
*/
60+
private final TypeBindings _parentBindings;
61+
62+
/*
63+
/**********************************************************
64+
/* Construction
65+
/**********************************************************
66+
*/
67+
68+
public TypeBindings(TypeFactory typeFactory, ClassStack stack, Class<?> cc)
69+
{
70+
this(typeFactory, null, stack, cc, null);
71+
}
72+
73+
public TypeBindings(TypeFactory typeFactory, ClassStack stack, JavaType type)
74+
{
75+
this(typeFactory, null, stack, type.getRawClass(), type);
76+
}
77+
78+
/**
79+
* Constructor used to create "child" instances; mostly to
80+
* allow delegation from explicitly defined local overrides
81+
* (local type variables for methods, constructors) to
82+
* contextual (class-defined) ones.
83+
*/
84+
public TypeBindings childInstance() {
85+
return new TypeBindings(_typeFactory, this, _classStack, _contextClass, _contextType);
86+
}
87+
88+
private TypeBindings(TypeFactory tf, TypeBindings parent, ClassStack stack,
89+
Class<?> cc, JavaType type)
90+
{
91+
_typeFactory = tf;
92+
_parentBindings = parent;
93+
_classStack = stack;
94+
_contextClass = cc;
95+
_contextType = type;
96+
}
97+
98+
/*
99+
/**********************************************************
100+
/* Pass-through type resolution methods
101+
/**********************************************************
102+
*/
103+
104+
public JavaType resolveType(Class<?> cls) {
105+
return _typeFactory._constructType(_classStack, cls, this);
106+
}
107+
108+
public JavaType resolveType(Type type) {
109+
return _typeFactory._constructType(_classStack, type, this);
110+
}
111+
112+
/*
113+
/**********************************************************
114+
/* Accesors
115+
/**********************************************************
116+
*/
117+
118+
public JavaType findType(String name, boolean mustFind)
119+
{
120+
if (_bindings == null) {
121+
_resolve();
122+
}
123+
JavaType t = _bindings.get(name);
124+
if (t != null) {
125+
return t;
126+
}
127+
if (_placeholders != null && _placeholders.contains(name)) {
128+
return UNBOUND;
129+
}
130+
if (_parentBindings != null) {
131+
return _parentBindings.findType(name, mustFind);
132+
}
133+
// nothing found, so...
134+
// Should we throw an exception or just return null?
135+
136+
/* 18-Feb-2011, tatu: There are some tricky type bindings within
137+
* java.util, such as HashMap$KeySet; so let's punt the problem
138+
* (honestly not sure what to do -- they are unbound for good, I think)
139+
*/
140+
if (_contextClass != null) {
141+
if (ClassUtil.getEnclosingClass(_contextClass) != null) {
142+
// [JACKSON-572]: Actually, let's skip this for all non-static inner classes
143+
// (which will also cover 'java.util' type cases...
144+
if (!Modifier.isStatic(_contextClass.getModifiers())) {
145+
return UNBOUND;
146+
}
147+
}
148+
}
149+
150+
if (!mustFind) {
151+
return null;
152+
}
153+
154+
String className;
155+
if (_contextClass != null) {
156+
className = _contextClass.getName();
157+
} else if (_contextType != null) {
158+
className = _contextType.toString();
159+
} else {
160+
className = "UNKNOWN";
161+
}
162+
throw new IllegalArgumentException("Type variable '"+name
163+
+"' can not be resolved (with context of class "+className+")");
164+
//t = UNBOUND;
165+
}
166+
167+
public void addBinding(String name, JavaType type)
168+
{
169+
// note: emptyMap() is unmodifiable, hence second check is needed:
170+
if (_bindings == null || _bindings.size() == 0) {
171+
_bindings = new LinkedHashMap<String,JavaType>();
172+
}
173+
_bindings.put(name, type);
174+
}
175+
176+
public JavaType[] typesAsArray()
177+
{
178+
if (_bindings == null) {
179+
_resolve();
180+
}
181+
if (_bindings.size() == 0) {
182+
return NO_TYPES;
183+
}
184+
return _bindings.values().toArray(new JavaType[_bindings.size()]);
185+
}
186+
187+
/*
188+
/**********************************************************
189+
/* Internal methods
190+
/**********************************************************
191+
*/
192+
193+
// Only for tests!
194+
protected int getBindingCount() {
195+
if (_bindings == null) {
196+
_resolve();
197+
}
198+
return _bindings.size();
199+
}
200+
201+
protected void _resolve()
202+
{
203+
_resolveBindings(_contextClass);
204+
205+
// finally: may have root level type info too
206+
if (_contextType != null) {
207+
int count = _contextType.containedTypeCount();
208+
if (count > 0) {
209+
for (int i = 0; i < count; ++i) {
210+
String name = _contextType.containedTypeName(i);
211+
JavaType type = _contextType.containedType(i);
212+
addBinding(name, type);
213+
}
214+
}
215+
}
216+
217+
// nothing bound? mark with empty map to prevent further calls
218+
if (_bindings == null) {
219+
_bindings = Collections.emptyMap();
220+
}
221+
}
222+
223+
public void _addPlaceholder(String name) {
224+
if (_placeholders == null) {
225+
_placeholders = new HashSet<String>();
226+
}
227+
_placeholders.add(name);
228+
}
229+
230+
protected void _resolveBindings(Type t)
231+
{
232+
if (t == null) return;
233+
234+
Class<?> raw;
235+
if (t instanceof ParameterizedType) {
236+
ParameterizedType pt = (ParameterizedType) t;
237+
Type[] args = pt.getActualTypeArguments();
238+
if (args != null && args.length > 0) {
239+
Class<?> rawType = (Class<?>) pt.getRawType();
240+
TypeVariable<?>[] vars = rawType.getTypeParameters();
241+
if (vars.length != args.length) {
242+
throw new IllegalArgumentException("Strange parametrized type (in class "+rawType.getName()+"): number of type arguments != number of type parameters ("+args.length+" vs "+vars.length+")");
243+
}
244+
for (int i = 0, len = args.length; i < len; ++i) {
245+
TypeVariable<?> var = vars[i];
246+
String name = var.getName();
247+
if (_bindings == null) {
248+
_bindings = new LinkedHashMap<String,JavaType>();
249+
} else {
250+
// 24-Mar-2010, tatu: Better ensure that we do not overwrite something
251+
// collected earlier (since we descend towards super-classes):
252+
if (_bindings.containsKey(name)) continue;
253+
}
254+
// first: add a placeholder to prevent infinite loops
255+
_addPlaceholder(name);
256+
// then resolve type
257+
_bindings.put(name, _typeFactory._constructType(_classStack, args[i], this));
258+
}
259+
}
260+
raw = (Class<?>)pt.getRawType();
261+
} else if (t instanceof Class<?>) {
262+
raw = (Class<?>) t;
263+
/* [JACKSON-677]: If this is an inner class then the generics are defined on the
264+
* enclosing class so we have to check there as well. We don't
265+
* need to call getEnclosingClass since anonymous classes declare
266+
* generics
267+
*/
268+
Class<?> decl = ClassUtil.getDeclaringClass(raw);
269+
/* 08-Feb-2013, tatu: Except that if context is also super-class, we must
270+
* skip it; context will be checked anyway, and we'd get StackOverflow if
271+
* we went there.
272+
*/
273+
if (decl != null && !decl.isAssignableFrom(raw)) {
274+
_resolveBindings(decl);
275+
}
276+
277+
/* 24-Mar-2010, tatu: Can not have true generics definitions, but can
278+
* have lower bounds ("<T extends BeanBase>") in declaration itself
279+
*/
280+
TypeVariable<?>[] vars = raw.getTypeParameters();
281+
if (vars != null && vars.length > 0) {
282+
JavaType[] typeParams = null;
283+
284+
if (_contextType != null && raw.isAssignableFrom(_contextType.getRawClass())) {
285+
typeParams = _typeFactory.findTypeParameters(_contextType, raw);
286+
}
287+
288+
for (int i = 0; i < vars.length; i++) {
289+
TypeVariable<?> var = vars[i];
290+
291+
String name = var.getName();
292+
Type varType = var.getBounds()[0];
293+
if (varType != null) {
294+
if (_bindings == null) {
295+
_bindings = new LinkedHashMap<String,JavaType>();
296+
} else { // and no overwriting...
297+
if (_bindings.containsKey(name)) continue;
298+
}
299+
_addPlaceholder(name); // to prevent infinite loops
300+
301+
if (typeParams != null && typeParams.length > i) {
302+
_bindings.put(name, typeParams[i]);
303+
} else {
304+
_bindings.put(name, _typeFactory._constructType(_classStack, varType, this));
305+
}
306+
}
307+
}
308+
}
309+
} else { // probably can't be any of these... so let's skip for now
310+
//if (type instanceof GenericArrayType) {
311+
//if (type instanceof TypeVariable<?>) {
312+
// if (type instanceof WildcardType) {
313+
return;
314+
}
315+
// but even if it's not a parameterized type, its super types may be:
316+
_resolveBindings(ClassUtil.getGenericSuperclass(raw));
317+
for (Type intType : raw.getGenericInterfaces()) {
318+
_resolveBindings(intType);
319+
}
320+
}
321+
322+
@Override
323+
public String toString()
324+
{
325+
if (_bindings == null) {
326+
_resolve();
327+
}
328+
StringBuilder sb = new StringBuilder("[TypeBindings for ");
329+
if (_contextType != null) {
330+
sb.append(_contextType.toString());
331+
} else {
332+
sb.append(_contextClass.getName());
333+
}
334+
sb.append(": ").append(_bindings).append("]");
335+
return sb.toString();
336+
}
337+
}

release-notes/VERSION

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Project: jackson-databind
66

77
2.7.0 (not yet released)
88

9+
#76: Problem handling datatypes Recursive type parameters
10+
(reported by Aram K)
911
#819: Add support for setting `FormatFeature` via `ObjectReader`, `ObjectWriter`
1012
#918: Add `MapperFeature.ALLOW_EXPLICIT_PROPERTY_RENAMING`
1113
(contributed by David H)

src/main/java/com/fasterxml/jackson/databind/BeanDescription.java

+3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ protected BeanDescription(JavaType type) {
7979
* Accessor for type bindings that may be needed to fully resolve
8080
* types of member object, such as return and argument types of
8181
* methods and constructors, and types of fields.
82+
*
83+
* @deprecated Since 2.7, use {@link #resolveType(java.lang.reflect.Type)} instead.
8284
*/
85+
@Deprecated
8386
public abstract TypeBindings bindingsForBeanType();
8487

8588
/**

0 commit comments

Comments
 (0)