Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement #25: add support for single-int Constructors #123

Merged
merged 5 commits into from
Feb 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -32,11 +32,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 @@ -45,28 +45,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 @@ -78,7 +85,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