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

Basic support of Java 16 records #312

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
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
12 changes: 9 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@
<version>1.20</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down Expand Up @@ -121,8 +127,7 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<release>16</release>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
Expand Down Expand Up @@ -150,7 +155,7 @@
<goal>jar</goal>
</goals>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
<doclint>none</doclint>
</configuration>
</execution>
</executions>
Expand Down Expand Up @@ -196,6 +201,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<configuration>
<testFailureIgnore>true</testFailureIgnore>
<parallel>methods</parallel>
<threadCount>1</threadCount>
<reuseForks>false</reuseForks>
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/jsoniter/ReflectionDecoderFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ public static Decoder create(ClassInfo classAndArgs) {
if (clazz.isEnum()) {
return new ReflectionEnumDecoder(clazz);
}
if (clazz.isRecord()) {
return new ReflectionRecordDecoder(classAndArgs).create();
}
return new ReflectionObjectDecoder(classAndArgs).create();
}
}
37 changes: 19 additions & 18 deletions src/main/java/com/jsoniter/ReflectionObjectDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@

class ReflectionObjectDecoder {

private static Object NOT_SET = new Object() {
protected static Object NOT_SET = new Object() {
@Override
public String toString() {
return "NOT_SET";
}
};
private Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>();
private String tempCacheKey;
private String ctorArgsCacheKey;
private int tempCount;
private long expectedTracker;
private int requiredIdx;
private int tempIdx;
private ClassDescriptor desc;
protected Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>();
protected String tempCacheKey;
protected String ctorArgsCacheKey;
protected int tempCount;
protected long expectedTracker;
protected int requiredIdx;
protected int tempIdx;
protected ClassDescriptor desc;

public ReflectionObjectDecoder(ClassInfo classInfo) {
try {
Expand All @@ -34,7 +34,8 @@ public ReflectionObjectDecoder(ClassInfo classInfo) {
}
}

private final void init(ClassInfo classInfo) throws Exception {
protected final void init(ClassInfo classInfo) throws Exception {

Class clazz = classInfo.clazz;
ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(classInfo, true);
for (Binding param : desc.ctor.parameters) {
Expand Down Expand Up @@ -346,7 +347,7 @@ private void setToBinding(Object obj, Binding binding, Object value) throws Exce
}
}

private void setExtra(Object obj, Map<String, Object> extra) throws Exception {
protected void setExtra(Object obj, Map<String, Object> extra) throws Exception {
if (extra == null) {
return;
}
Expand All @@ -367,24 +368,24 @@ private void setExtra(Object obj, Map<String, Object> extra) throws Exception {
}
}

private boolean canNotSetDirectly(Binding binding) {
protected boolean canNotSetDirectly(Binding binding) {
return binding.field == null && binding.method == null;
}

private Object decodeBinding(JsonIterator iter, Binding binding) throws Exception {
protected Object decodeBinding(JsonIterator iter, Binding binding) throws Exception {
Object value;
value = binding.decoder.decode(iter);
return value;
}

private Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception {
protected Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception {
if (binding.valueCanReuse) {
CodegenAccess.setExistingObject(iter, binding.field.get(obj));
}
return decodeBinding(iter, binding);
}

private Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName, Map<String, Object> extra) throws IOException {
protected Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName, Map<String, Object> extra) throws IOException {
boolean shouldReadValue = desc.asExtraForUnknownProperties || !desc.keyValueTypeWrappers.isEmpty();
if (shouldReadValue) {
Any value = iter.readAny();
Expand All @@ -398,7 +399,7 @@ private Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName
return extra;
}

private List<String> collectMissingFields(long tracker) {
protected List<String> collectMissingFields(long tracker) {
List<String> missingFields = new ArrayList<String>();
for (Binding binding : allBindings.values()) {
if (binding.asMissingWhenNotPresent) {
Expand All @@ -409,7 +410,7 @@ private List<String> collectMissingFields(long tracker) {
return missingFields;
}

private void applyWrappers(Object[] temp, Object obj) throws Exception {
protected void applyWrappers(Object[] temp, Object obj) throws Exception {
for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) {
Object[] args = new Object[wrapper.parameters.size()];
for (int i = 0; i < wrapper.parameters.size(); i++) {
Expand All @@ -422,7 +423,7 @@ private void applyWrappers(Object[] temp, Object obj) throws Exception {
}
}

private Object createNewObject(JsonIterator iter, Object[] temp) throws Exception {
protected Object createNewObject(JsonIterator iter, Object[] temp) throws Exception {
if (iter.tempObjects == null) {
iter.tempObjects = new HashMap<String, Object>();
}
Expand Down
113 changes: 113 additions & 0 deletions src/main/java/com/jsoniter/ReflectionRecordDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.jsoniter;

import com.jsoniter.spi.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ReflectionRecordDecoder extends ReflectionObjectDecoder {

private boolean useOnlyFieldRecord = false;

public ReflectionRecordDecoder(ClassInfo classInfo) {

super(classInfo);

if (desc.clazz.isRecord() && !desc.fields.isEmpty() && tempCount == 0) {
tempCount = tempIdx;
tempCacheKey = "temp@" + desc.clazz.getName();
ctorArgsCacheKey = "ctor@" + desc.clazz.getName();

desc.ctor.parameters.addAll(desc.fields);
useOnlyFieldRecord = true;
}
}

@Override
public Decoder create() {

if (useOnlyFieldRecord)
return new OnlyFieldRecord();

if (desc.ctor.parameters.isEmpty()) {
if (desc.bindingTypeWrappers.isEmpty()) {
return new OnlyFieldRecord();
} else {
return new WithWrapper();
}
} else {
return new WithCtor();
}
}

public class OnlyFieldRecord implements Decoder {

@Override
public Object decode(JsonIterator iter) throws IOException {

try {
return decode_(iter);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new JsonException(e);
}
}

private Object decode_(JsonIterator iter) throws Exception {
if (iter.readNull()) {
CodegenAccess.resetExistingObject(iter);
return null;
}
if (iter.tempObjects == null) {
iter.tempObjects = new HashMap<String, Object>();
}
Object[] temp = (Object[]) iter.tempObjects.get(tempCacheKey);
if (temp == null) {
temp = new Object[tempCount];
iter.tempObjects.put(tempCacheKey, temp);
}
Arrays.fill(temp, NOT_SET);
if (!CodegenAccess.readObjectStart(iter)) {
if (requiredIdx > 0) {
throw new JsonException("missing required properties: " + collectMissingFields(0));
}
return createNewObject(iter, temp);
}
Map<String, Object> extra = null;
long tracker = 0L;
Slice fieldName = CodegenAccess.readObjectFieldAsSlice(iter);
Binding binding = allBindings.get(fieldName);
if (binding == null) {
extra = onUnknownProperty(iter, fieldName, extra);
} else {
if (binding.asMissingWhenNotPresent) {
tracker |= binding.mask;
}
temp[binding.idx] = decodeBinding(iter, binding);
}
while (CodegenAccess.nextToken(iter) == ',') {
fieldName = CodegenAccess.readObjectFieldAsSlice(iter);
binding = allBindings.get(fieldName);
if (binding == null) {
extra = onUnknownProperty(iter, fieldName, extra);
} else {
if (binding.asMissingWhenNotPresent) {
tracker |= binding.mask;
}
temp[binding.idx] = decodeBinding(iter, binding);
}
}
if (tracker != expectedTracker) {
throw new JsonException("missing required properties: " + collectMissingFields(tracker));
}
Object obj = createNewObject(iter, temp.clone());
setExtra(obj, extra);
applyWrappers(temp, obj);
return obj;
}

}
}
14 changes: 12 additions & 2 deletions src/main/java/com/jsoniter/spi/ClassDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static ClassDescriptor getDecodingClassDescriptor(ClassInfo classInfo, bo
desc.classInfo = classInfo;
desc.clazz = clazz;
desc.lookup = lookup;
desc.ctor = getCtor(clazz);
desc.ctor = clazz.isRecord() ? getRecordCtor(clazz) : getCtor(clazz);
desc.setters = getSetters(lookup, classInfo, includingPrivate);
desc.getters = new ArrayList<Binding>();
desc.fields = getFields(lookup, classInfo, includingPrivate);
Expand Down Expand Up @@ -203,6 +203,17 @@ private static ConstructorDescriptor getCtor(Class clazz) {
return cctor;
}

private static ConstructorDescriptor getRecordCtor(Class<?> clazz) {
ConstructorDescriptor cctor = new ConstructorDescriptor();
try {
Class<?>[] canonicalParameterTypes = Arrays.stream(clazz.getRecordComponents()).map(RecordComponent::getType).toArray(Class<?>[]::new);
cctor.ctor = clazz.getDeclaredConstructor(canonicalParameterTypes);
} catch (Exception e) {
cctor.ctor = null;
}
return cctor;
}

private static List<Binding> getFields(Map<String, Type> lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList<Binding> bindings = new ArrayList<Binding>();
for (Field field : getAllFields(classInfo.clazz)) {
Expand Down Expand Up @@ -432,7 +443,6 @@ public List<Binding> allDecoderBindings() {
return bindings;
}


public List<Binding> allEncoderBindings() {
ArrayList<Binding> bindings = new ArrayList<Binding>(8);
bindings.addAll(fields);
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/com/jsoniter/IterImplForStreamingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
import org.apache.commons.lang3.NotImplementedException;
import org.junit.experimental.categories.Category;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

public class IterImplForStreamingTest extends TestCase {

Expand Down Expand Up @@ -77,7 +77,7 @@ private static InputStream getSluggishInputStream(final byte[] src) {

@Override
public int read() throws IOException {
throw new NotImplementedException();
throw new NotImplementedException(new Exception());
}

@Override
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/com/jsoniter/SimpleRecord.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.jsoniter;

public record SimpleRecord(String field1, String field2) {
public SimpleRecord() {
this(null, null);
}
public SimpleRecord(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
}
Loading