Skip to content

Commit 380913d

Browse files
cowtowncoderShounaks
authored andcommitted
Implement FasterXML#25: add support for single-int Constructors (FasterXML#123)
1 parent 28ae21b commit 380913d

File tree

9 files changed

+191
-102
lines changed

9 files changed

+191
-102
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ You can use Maven dependency like:
241241
<dependency>
242242
<groupId>com.fasterxml.jackson.jr</groupId>
243243
<artifactId>jackson-jr-objects</artifactId>
244-
<version>2.13.0</version>
244+
<version>2.16.1</version>
245245
</dependency>
246246
```
247247

jr-annotation-support/src/main/java/com/fasterxml/jackson/jr/annotationsupport/AnnotationBasedIntrospector.java

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
1313

1414
import com.fasterxml.jackson.jr.ob.JSON;
15+
import com.fasterxml.jackson.jr.ob.impl.BeanConstructors;
1516
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
1617
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;
1718
import com.fasterxml.jackson.jr.ob.impl.POJODefinition;
@@ -43,7 +44,7 @@ public class AnnotationBasedIntrospector
4344
protected int _features;
4445

4546
protected AnnotationBasedIntrospector(Class<?> type, boolean serialization,
46-
JsonAutoDetect.Value visibility, int features) {
47+
JsonAutoDetect.Value visibility, int features) {
4748
_type = type;
4849
_forSerialization = serialization;
4950
_ignorableNames = serialization ? null : new HashSet<String>();
@@ -82,31 +83,33 @@ protected POJODefinition introspectDefinition()
8283
_findFields();
8384
_findMethods();
8485

85-
Constructor<?> defaultCtor = null;
86-
Constructor<?> stringCtor = null;
87-
Constructor<?> longCtor = null;
86+
final BeanConstructors constructors;
8887

8988
// A few things only matter during deserialization: constructors,
9089
// secondary ignoral information:
91-
if (!_forSerialization) {
90+
if (_forSerialization) {
91+
constructors = null;
92+
} else {
93+
constructors = new BeanConstructors(_type);
9294
for (Constructor<?> ctor : _type.getDeclaredConstructors()) {
9395
Class<?>[] argTypes = ctor.getParameterTypes();
9496
if (argTypes.length == 0) {
95-
defaultCtor = ctor;
97+
constructors.addNoArgsConstructor(ctor);
9698
} else if (argTypes.length == 1) {
9799
Class<?> argType = argTypes[0];
98100
if (argType == String.class) {
99-
stringCtor = ctor;
101+
constructors.addStringConstructor(ctor);
102+
} else if (argType == Integer.class || argType == Integer.TYPE) {
103+
constructors.addIntConstructor(ctor);
100104
} else if (argType == Long.class || argType == Long.TYPE) {
101-
longCtor = ctor;
105+
constructors.addLongConstructor(ctor);
102106
}
103107
}
104108
}
105109
}
106110

107111
POJODefinition def = new POJODefinition(_type,
108-
_pruneProperties(_forSerialization),
109-
defaultCtor, stringCtor, longCtor);
112+
_pruneProperties(_forSerialization), constructors);
110113
if (_ignorableNames != null) {
111114
def = def.withIgnorals(_ignorableNames);
112115
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package com.fasterxml.jackson.jr.ob.impl;
2+
3+
import java.lang.reflect.Constructor;
4+
5+
/**
6+
* Container class added to encapsulate details of collection and use of
7+
* Constructors for User-defined (non-JDK) types (aka "Beans").
8+
*
9+
* @since 2.17
10+
*/
11+
public class BeanConstructors
12+
{
13+
protected final Class<?> _valueType;
14+
15+
protected Constructor<?> _noArgsCtor;
16+
17+
protected Constructor<?> _intCtor;
18+
protected Constructor<?> _longCtor;
19+
protected Constructor<?> _stringCtor;
20+
21+
public BeanConstructors(Class<?> valueType) {
22+
_valueType = valueType;
23+
}
24+
25+
public BeanConstructors addNoArgsConstructor(Constructor<?> ctor) {
26+
_noArgsCtor = ctor;
27+
return this;
28+
}
29+
30+
public BeanConstructors addIntConstructor(Constructor<?> ctor) {
31+
_intCtor = ctor;
32+
return this;
33+
}
34+
35+
public BeanConstructors addLongConstructor(Constructor<?> ctor) {
36+
_longCtor = ctor;
37+
return this;
38+
}
39+
40+
public BeanConstructors addStringConstructor(Constructor<?> ctor) {
41+
_stringCtor = ctor;
42+
return this;
43+
}
44+
45+
public void forceAccess() {
46+
if (_noArgsCtor != null) {
47+
_noArgsCtor.setAccessible(true);
48+
}
49+
if (_intCtor != null) {
50+
_intCtor.setAccessible(true);
51+
}
52+
if (_longCtor != null) {
53+
_longCtor.setAccessible(true);
54+
}
55+
if (_stringCtor != null) {
56+
_stringCtor.setAccessible(true);
57+
}
58+
}
59+
60+
protected Object create() throws Exception {
61+
if (_noArgsCtor == null) {
62+
throw new IllegalStateException("Class "+_valueType.getName()+" does not have default constructor to use");
63+
}
64+
return _noArgsCtor.newInstance((Object[]) null);
65+
}
66+
67+
protected Object create(String str) throws Exception {
68+
if (_stringCtor == null) {
69+
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-String constructor to use");
70+
}
71+
return _stringCtor.newInstance(str);
72+
}
73+
74+
protected Object create(long l) throws Exception {
75+
// 23-Feb-2024, tatu: As per [jackson-jr#25] can have `int`-constructors too.
76+
// For now no need to try to optimize separately
77+
if (_longCtor != null) {
78+
return _longCtor.newInstance(l);
79+
}
80+
if (_intCtor != null) {
81+
// TODO: should this do bounds checks?
82+
return _intCtor.newInstance((int) l);
83+
}
84+
throw new IllegalStateException("Class "+_valueType.getName()
85+
+" does not have single-long or single-int constructor to use");
86+
}
87+
}

jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanPropertyIntrospector.java

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,11 @@ public BeanPropertyIntrospector() { }
3535
public static BeanPropertyIntrospector instance() { return INSTANCE; }
3636

3737
public POJODefinition pojoDefinitionForDeserialization(JSONReader r, Class<?> pojoType) {
38-
return _construct(pojoType, r.features());
38+
return _introspectDefinition(pojoType, false, r.features());
3939
}
4040

4141
public POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class<?> pojoType) {
42-
return _construct(pojoType, w.features());
42+
return _introspectDefinition(pojoType, true, w.features());
4343
}
4444

4545
/*
@@ -48,28 +48,35 @@ public POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class<?> pojo
4848
/**********************************************************************
4949
*/
5050

51-
private POJODefinition _construct(Class<?> beanType, int features)
51+
private POJODefinition _introspectDefinition(Class<?> beanType,
52+
boolean forSerialization, int features)
5253
{
5354
Map<String,PropBuilder> propsByName = new TreeMap<>();
5455
_introspect(beanType, propsByName, features);
5556

56-
Constructor<?> defaultCtor = null;
57-
Constructor<?> stringCtor = null;
58-
Constructor<?> longCtor = null;
59-
60-
for (Constructor<?> ctor : beanType.getDeclaredConstructors()) {
61-
Class<?>[] argTypes = ctor.getParameterTypes();
62-
if (argTypes.length == 0) {
63-
defaultCtor = ctor;
64-
} else if (argTypes.length == 1) {
65-
Class<?> argType = argTypes[0];
66-
if (argType == String.class) {
67-
stringCtor = ctor;
68-
} else if (argType == Long.class || argType == Long.TYPE) {
69-
longCtor = ctor;
57+
final BeanConstructors constructors;
58+
59+
if (forSerialization) {
60+
constructors = null;
61+
} else {
62+
constructors = new BeanConstructors(beanType);
63+
for (Constructor<?> ctor : beanType.getDeclaredConstructors()) {
64+
Class<?>[] argTypes = ctor.getParameterTypes();
65+
if (argTypes.length == 0) {
66+
constructors.addNoArgsConstructor(ctor);
67+
} else if (argTypes.length == 1) {
68+
Class<?> argType = argTypes[0];
69+
if (argType == String.class) {
70+
constructors.addStringConstructor(ctor);
71+
} else if (argType == Integer.class || argType == Integer.TYPE) {
72+
constructors.addIntConstructor(ctor);
73+
} else if (argType == Long.class || argType == Long.TYPE) {
74+
constructors.addLongConstructor(ctor);
75+
}
7076
}
7177
}
7278
}
79+
7380
final int len = propsByName.size();
7481
Prop[] props;
7582
if (len == 0) {
@@ -81,7 +88,7 @@ private POJODefinition _construct(Class<?> beanType, int features)
8188
props[i++] = builder.build();
8289
}
8390
}
84-
return new POJODefinition(beanType, props, defaultCtor, stringCtor, longCtor);
91+
return new POJODefinition(beanType, props, constructors);
8592
}
8693

8794
private static void _introspect(Class<?> currType, Map<String, PropBuilder> props,

jr-objects/src/main/java/com/fasterxml/jackson/jr/ob/impl/BeanReader.java

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,23 @@ public class BeanReader
3131
// @since 2.11
3232
protected final Set<String> _ignorableNames;
3333

34-
protected final Constructor<?> _defaultCtor;
35-
protected final Constructor<?> _stringCtor;
36-
protected final Constructor<?> _longCtor;
34+
/**
35+
* @since 2.17
36+
*/
37+
protected final BeanConstructors _constructors;
3738

3839
/**
3940
* Constructors used for deserialization use case
41+
*
42+
* @since 2.17
4043
*/
4144
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
42-
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor,
45+
BeanConstructors constructors,
4346
Set<String> ignorableNames, Map<String, String> aliasMapping)
4447
{
4548
super(type);
4649
_propsByName = props;
47-
_defaultCtor = defaultCtor;
48-
_stringCtor = stringCtor;
49-
_longCtor = longCtor;
50+
_constructors = constructors;
5051
if (ignorableNames == null) {
5152
ignorableNames = Collections.<String>emptySet();
5253
}
@@ -57,6 +58,17 @@ public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
5758
_aliasMapping = aliasMapping;
5859
}
5960

61+
@Deprecated // since 2.17
62+
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
63+
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor,
64+
Set<String> ignorableNames, Map<String, String> aliasMapping)
65+
{
66+
this(type, props, new BeanConstructors(type).addNoArgsConstructor(defaultCtor)
67+
.addStringConstructor(stringCtor)
68+
.addLongConstructor(longCtor),
69+
ignorableNames, aliasMapping);
70+
}
71+
6072
@Deprecated // since 2.11
6173
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
6274
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor) {
@@ -87,15 +99,15 @@ public Object readNext(JSONReader r, JsonParser p) throws IOException
8799
case VALUE_NULL:
88100
return null;
89101
case VALUE_STRING:
90-
return create(p.getText());
102+
return _constructors.create(p.getText());
91103
case VALUE_NUMBER_INT:
92-
return create(p.getLongValue());
104+
return _constructors.create(p.getLongValue());
93105
case START_OBJECT:
94106
{
95-
Object bean = create();
107+
Object bean = _constructors.create();
96108
final Object[] valueBuf = r._setterBuffer;
97109
String propName;
98-
110+
99111
for (; (propName = p.nextFieldName()) != null; ) {
100112
BeanPropertyReader prop = findProperty(propName);
101113
if (prop == null) {
@@ -138,15 +150,15 @@ public Object read(JSONReader r, JsonParser p) throws IOException
138150
case VALUE_NULL:
139151
return null;
140152
case VALUE_STRING:
141-
return create(p.getText());
153+
return _constructors.create(p.getText());
142154
case VALUE_NUMBER_INT:
143-
return create(p.getLongValue());
155+
return _constructors.create(p.getLongValue());
144156
case START_OBJECT:
145157
{
146-
Object bean = create();
158+
Object bean = _constructors.create();
147159
String propName;
148160
final Object[] valueBuf = r._setterBuffer;
149-
161+
150162
for (; (propName = p.nextFieldName()) != null; ) {
151163
BeanPropertyReader prop = findProperty(propName);
152164
if (prop == null) {
@@ -159,8 +171,7 @@ public Object read(JSONReader r, JsonParser p) throws IOException
159171
// also verify we are not confused...
160172
if (!p.hasToken(JsonToken.END_OBJECT)) {
161173
throw _reportProblem(p);
162-
}
163-
174+
}
164175
return bean;
165176
}
166177
default:
@@ -175,27 +186,6 @@ public Object read(JSONReader r, JsonParser p) throws IOException
175186
throw JSONObjectException.from(p, "Can not create a %s instance out of %s",
176187
_valueType.getName(), _tokenDesc(p));
177188
}
178-
179-
protected Object create() throws Exception {
180-
if (_defaultCtor == null) {
181-
throw new IllegalStateException("Class "+_valueType.getName()+" does not have default constructor to use");
182-
}
183-
return _defaultCtor.newInstance((Object[]) null);
184-
}
185-
186-
protected Object create(String str) throws Exception {
187-
if (_stringCtor == null) {
188-
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-String constructor to use");
189-
}
190-
return _stringCtor.newInstance(str);
191-
}
192-
193-
protected Object create(long l) throws Exception {
194-
if (_longCtor == null) {
195-
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-long constructor to use");
196-
}
197-
return _longCtor.newInstance(l);
198-
}
199189

200190
protected void handleUnknown(JSONReader reader, JsonParser parser, String fieldName) throws IOException {
201191
if (JSON.Feature.FAIL_ON_UNKNOWN_BEAN_PROPERTY.isEnabled(reader._features)) {

0 commit comments

Comments
 (0)