Skip to content

Commit

Permalink
Implement FasterXML#25: add support for single-int Constructors (Fast…
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder authored and Shounaks committed Feb 27, 2024
1 parent 28ae21b commit 380913d
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 102 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ You can use Maven dependency like:
<dependency>
<groupId>com.fasterxml.jackson.jr</groupId>
<artifactId>jackson-jr-objects</artifactId>
<version>2.13.0</version>
<version>2.16.1</version>
</dependency>
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import com.fasterxml.jackson.jr.ob.JSON;
import com.fasterxml.jackson.jr.ob.impl.BeanConstructors;
import com.fasterxml.jackson.jr.ob.impl.JSONReader;
import com.fasterxml.jackson.jr.ob.impl.JSONWriter;
import com.fasterxml.jackson.jr.ob.impl.POJODefinition;
Expand Down Expand Up @@ -43,7 +44,7 @@ public class AnnotationBasedIntrospector
protected int _features;

protected AnnotationBasedIntrospector(Class<?> type, boolean serialization,
JsonAutoDetect.Value visibility, int features) {
JsonAutoDetect.Value visibility, int features) {
_type = type;
_forSerialization = serialization;
_ignorableNames = serialization ? null : new HashSet<String>();
Expand Down Expand Up @@ -82,31 +83,33 @@ protected POJODefinition introspectDefinition()
_findFields();
_findMethods();

Constructor<?> defaultCtor = null;
Constructor<?> stringCtor = null;
Constructor<?> longCtor = null;
final BeanConstructors constructors;

// A few things only matter during deserialization: constructors,
// secondary ignoral information:
if (!_forSerialization) {
if (_forSerialization) {
constructors = null;
} else {
constructors = new BeanConstructors(_type);
for (Constructor<?> ctor : _type.getDeclaredConstructors()) {
Class<?>[] argTypes = ctor.getParameterTypes();
if (argTypes.length == 0) {
defaultCtor = ctor;
constructors.addNoArgsConstructor(ctor);
} else if (argTypes.length == 1) {
Class<?> argType = argTypes[0];
if (argType == String.class) {
stringCtor = ctor;
constructors.addStringConstructor(ctor);
} else if (argType == Integer.class || argType == Integer.TYPE) {
constructors.addIntConstructor(ctor);
} else if (argType == Long.class || argType == Long.TYPE) {
longCtor = ctor;
constructors.addLongConstructor(ctor);
}
}
}
}

POJODefinition def = new POJODefinition(_type,
_pruneProperties(_forSerialization),
defaultCtor, stringCtor, longCtor);
_pruneProperties(_forSerialization), constructors);
if (_ignorableNames != null) {
def = def.withIgnorals(_ignorableNames);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.fasterxml.jackson.jr.ob.impl;

import java.lang.reflect.Constructor;

/**
* Container class added to encapsulate details of collection and use of
* Constructors for User-defined (non-JDK) types (aka "Beans").
*
* @since 2.17
*/
public class BeanConstructors
{
protected final Class<?> _valueType;

protected Constructor<?> _noArgsCtor;

protected Constructor<?> _intCtor;
protected Constructor<?> _longCtor;
protected Constructor<?> _stringCtor;

public BeanConstructors(Class<?> valueType) {
_valueType = valueType;
}

public BeanConstructors addNoArgsConstructor(Constructor<?> ctor) {
_noArgsCtor = ctor;
return this;
}

public BeanConstructors addIntConstructor(Constructor<?> ctor) {
_intCtor = ctor;
return this;
}

public BeanConstructors addLongConstructor(Constructor<?> ctor) {
_longCtor = ctor;
return this;
}

public BeanConstructors addStringConstructor(Constructor<?> ctor) {
_stringCtor = ctor;
return this;
}

public void forceAccess() {
if (_noArgsCtor != null) {
_noArgsCtor.setAccessible(true);
}
if (_intCtor != null) {
_intCtor.setAccessible(true);
}
if (_longCtor != null) {
_longCtor.setAccessible(true);
}
if (_stringCtor != null) {
_stringCtor.setAccessible(true);
}
}

protected Object create() throws Exception {
if (_noArgsCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have default constructor to use");
}
return _noArgsCtor.newInstance((Object[]) null);
}

protected Object create(String str) throws Exception {
if (_stringCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-String constructor to use");
}
return _stringCtor.newInstance(str);
}

protected Object create(long l) throws Exception {
// 23-Feb-2024, tatu: As per [jackson-jr#25] can have `int`-constructors too.
// For now no need to try to optimize separately
if (_longCtor != null) {
return _longCtor.newInstance(l);
}
if (_intCtor != null) {
// TODO: should this do bounds checks?
return _intCtor.newInstance((int) l);
}
throw new IllegalStateException("Class "+_valueType.getName()
+" does not have single-long or single-int constructor to use");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ public BeanPropertyIntrospector() { }
public static BeanPropertyIntrospector instance() { return INSTANCE; }

public POJODefinition pojoDefinitionForDeserialization(JSONReader r, Class<?> pojoType) {
return _construct(pojoType, r.features());
return _introspectDefinition(pojoType, false, r.features());
}

public POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class<?> pojoType) {
return _construct(pojoType, w.features());
return _introspectDefinition(pojoType, true, w.features());
}

/*
Expand All @@ -48,28 +48,35 @@ public POJODefinition pojoDefinitionForSerialization(JSONWriter w, Class<?> pojo
/**********************************************************************
*/

private POJODefinition _construct(Class<?> beanType, int features)
private POJODefinition _introspectDefinition(Class<?> beanType,
boolean forSerialization, int features)
{
Map<String,PropBuilder> propsByName = new TreeMap<>();
_introspect(beanType, propsByName, features);

Constructor<?> defaultCtor = null;
Constructor<?> stringCtor = null;
Constructor<?> longCtor = null;

for (Constructor<?> ctor : beanType.getDeclaredConstructors()) {
Class<?>[] argTypes = ctor.getParameterTypes();
if (argTypes.length == 0) {
defaultCtor = ctor;
} else if (argTypes.length == 1) {
Class<?> argType = argTypes[0];
if (argType == String.class) {
stringCtor = ctor;
} else if (argType == Long.class || argType == Long.TYPE) {
longCtor = ctor;
final BeanConstructors constructors;

if (forSerialization) {
constructors = null;
} else {
constructors = new BeanConstructors(beanType);
for (Constructor<?> ctor : beanType.getDeclaredConstructors()) {
Class<?>[] argTypes = ctor.getParameterTypes();
if (argTypes.length == 0) {
constructors.addNoArgsConstructor(ctor);
} else if (argTypes.length == 1) {
Class<?> argType = argTypes[0];
if (argType == String.class) {
constructors.addStringConstructor(ctor);
} else if (argType == Integer.class || argType == Integer.TYPE) {
constructors.addIntConstructor(ctor);
} else if (argType == Long.class || argType == Long.TYPE) {
constructors.addLongConstructor(ctor);
}
}
}
}

final int len = propsByName.size();
Prop[] props;
if (len == 0) {
Expand All @@ -81,7 +88,7 @@ private POJODefinition _construct(Class<?> beanType, int features)
props[i++] = builder.build();
}
}
return new POJODefinition(beanType, props, defaultCtor, stringCtor, longCtor);
return new POJODefinition(beanType, props, constructors);
}

private static void _introspect(Class<?> currType, Map<String, PropBuilder> props,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,23 @@ public class BeanReader
// @since 2.11
protected final Set<String> _ignorableNames;

protected final Constructor<?> _defaultCtor;
protected final Constructor<?> _stringCtor;
protected final Constructor<?> _longCtor;
/**
* @since 2.17
*/
protected final BeanConstructors _constructors;

/**
* Constructors used for deserialization use case
*
* @since 2.17
*/
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor,
BeanConstructors constructors,
Set<String> ignorableNames, Map<String, String> aliasMapping)
{
super(type);
_propsByName = props;
_defaultCtor = defaultCtor;
_stringCtor = stringCtor;
_longCtor = longCtor;
_constructors = constructors;
if (ignorableNames == null) {
ignorableNames = Collections.<String>emptySet();
}
Expand All @@ -57,6 +58,17 @@ public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
_aliasMapping = aliasMapping;
}

@Deprecated // since 2.17
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor,
Set<String> ignorableNames, Map<String, String> aliasMapping)
{
this(type, props, new BeanConstructors(type).addNoArgsConstructor(defaultCtor)
.addStringConstructor(stringCtor)
.addLongConstructor(longCtor),
ignorableNames, aliasMapping);
}

@Deprecated // since 2.11
public BeanReader(Class<?> type, Map<String, BeanPropertyReader> props,
Constructor<?> defaultCtor, Constructor<?> stringCtor, Constructor<?> longCtor) {
Expand Down Expand Up @@ -87,15 +99,15 @@ public Object readNext(JSONReader r, JsonParser p) throws IOException
case VALUE_NULL:
return null;
case VALUE_STRING:
return create(p.getText());
return _constructors.create(p.getText());
case VALUE_NUMBER_INT:
return create(p.getLongValue());
return _constructors.create(p.getLongValue());
case START_OBJECT:
{
Object bean = create();
Object bean = _constructors.create();
final Object[] valueBuf = r._setterBuffer;
String propName;

for (; (propName = p.nextFieldName()) != null; ) {
BeanPropertyReader prop = findProperty(propName);
if (prop == null) {
Expand Down Expand Up @@ -138,15 +150,15 @@ public Object read(JSONReader r, JsonParser p) throws IOException
case VALUE_NULL:
return null;
case VALUE_STRING:
return create(p.getText());
return _constructors.create(p.getText());
case VALUE_NUMBER_INT:
return create(p.getLongValue());
return _constructors.create(p.getLongValue());
case START_OBJECT:
{
Object bean = create();
Object bean = _constructors.create();
String propName;
final Object[] valueBuf = r._setterBuffer;

for (; (propName = p.nextFieldName()) != null; ) {
BeanPropertyReader prop = findProperty(propName);
if (prop == null) {
Expand All @@ -159,8 +171,7 @@ public Object read(JSONReader r, JsonParser p) throws IOException
// also verify we are not confused...
if (!p.hasToken(JsonToken.END_OBJECT)) {
throw _reportProblem(p);
}

}
return bean;
}
default:
Expand All @@ -175,27 +186,6 @@ public Object read(JSONReader r, JsonParser p) throws IOException
throw JSONObjectException.from(p, "Can not create a %s instance out of %s",
_valueType.getName(), _tokenDesc(p));
}

protected Object create() throws Exception {
if (_defaultCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have default constructor to use");
}
return _defaultCtor.newInstance((Object[]) null);
}

protected Object create(String str) throws Exception {
if (_stringCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-String constructor to use");
}
return _stringCtor.newInstance(str);
}

protected Object create(long l) throws Exception {
if (_longCtor == null) {
throw new IllegalStateException("Class "+_valueType.getName()+" does not have single-long constructor to use");
}
return _longCtor.newInstance(l);
}

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

0 comments on commit 380913d

Please sign in to comment.