Skip to content

Commit

Permalink
Improved support for maps, lists and sets from Java8 processor
Browse files Browse the repository at this point in the history
Allow usage of such types without runtime mode.
Add MapAnalyzer.Runtime since Map<String, Object> does not work by default.
Improved error message: instruct user what to do in case of such errors.
Use optimized converter for collection generic arguments.
  • Loading branch information
zapov committed Sep 16, 2018
1 parent 688bee2 commit 2f07232
Show file tree
Hide file tree
Showing 17 changed files with 405 additions and 108 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.dslplatform.jackson;

import com.fasterxml.jackson.annotation.JsonCreator;

public class ImmutablePerson {

public final String firstName;
public final String lastName;
public final int age;

//JsonCreator can be used for selecting the appropriate constructor when there are multiple ones
@JsonCreator
public ImmutablePerson(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
}
29 changes: 29 additions & 0 deletions java8/src/main/java/com/dslplatform/json/processor/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import java.io.IOException;
import java.io.Writer;
import java.util.*;

Expand Down Expand Up @@ -80,4 +85,28 @@ static List<AttributeInfo> sortedAttributes(StructInfo info) {
}
return result;
}

static String extractRawType(TypeMirror type) {
if (type.getKind() == TypeKind.DECLARED) {
return ((DeclaredType) type).asElement().toString();
} else {
return type.toString();
}
}

void serializeKnownCollection(AttributeInfo attr) throws IOException {
if (attr.isArray) {
String content = extractRawType(((ArrayType) attr.type).getComponentType());
code.append("(").append(content).append("[])reader.readArray(reader_").append(attr.name);
code.append(", emptyArray_").append(attr.name).append(")");
} else if (attr.isList) {
code.append("reader.readCollection(reader_").append(attr.name).append(")");
} else if (attr.isSet) {
code.append("reader.readSet(reader_").append(attr.name).append(")");
} else if (attr.isMap) {
code.append("reader.readMap(key_reader_").append(attr.name).append(", value_reader_").append(attr.name).append(")");
} else {
throw new IllegalArgumentException("Unknown attribute collection " + attr.name);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.dslplatform.json.processor;

import com.dslplatform.json.CompiledJson;
import com.dslplatform.json.Nullable;

import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
Expand Down Expand Up @@ -115,19 +114,27 @@ private void asFormatConverter(final StructInfo si, final String name, final Str
boolean hasConverter = context.inlinedConverters.containsKey(typeName);
StructInfo target = context.structs.get(attr.typeName);
if (attr.converter == null && (target == null || target.converter == null) && !hasConverter && !isStaticEnum(attr) && !attr.isJsonObject) {
String content = attr.collectionContent(context.knownTypes);
List<String> types = attr.collectionContent(context.knownTypes);
if (target != null && attr.isEnum(context.structs)) {
code.append("\t\tprivate final ").append(findConverterName(target)).append(".EnumConverter converter_").append(attr.name).append(";\n");
} else if (content != null || (attr.isGeneric && !attr.containsStructOwnerType)) {
} else if (types != null && types.size() == 1 || (attr.isGeneric && !attr.containsStructOwnerType)) {
String content;
if (attr.isGeneric) {
if (attr.isArray) {
content = ((ArrayType) attr.type).getComponentType().toString();
} else {
content = attr.typeName;
}
} else {
content = types.get(0);
}
code.append("\t\tprivate final com.dslplatform.json.JsonReader.ReadObject<").append(content).append("> reader_").append(attr.name).append(";\n");
code.append("\t\tprivate final com.dslplatform.json.JsonWriter.WriteObject<").append(content).append("> writer_").append(attr.name).append(";\n");
} else if (types != null && types.size() == 2) {
code.append("\t\tprivate final com.dslplatform.json.JsonReader.ReadObject<").append(types.get(0)).append("> key_reader_").append(attr.name).append(";\n");
code.append("\t\tprivate final com.dslplatform.json.JsonReader.ReadObject<").append(types.get(1)).append("> value_reader_").append(attr.name).append(";\n");
code.append("\t\tprivate final com.dslplatform.json.JsonWriter.WriteObject<").append(types.get(0)).append("> key_writer_").append(attr.name).append(";\n");
code.append("\t\tprivate final com.dslplatform.json.JsonWriter.WriteObject<").append(types.get(1)).append("> value_writer_").append(attr.name).append(";\n");
} else {
String type;
if (attr.isGeneric) {
Expand All @@ -142,7 +149,7 @@ private void asFormatConverter(final StructInfo si, final String name, final Str
code.append("\t\t\t\tjava.lang.reflect.Type manifest = ").append(type).append(";\n");
code.append("\t\t\t\treader_").append(attr.name).append(" = __dsljson.tryFindReader(manifest);\n");
code.append("\t\t\t\tif (reader_").append(attr.name).append(" == null) {\n");
code.append("\t\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find reader for \" + manifest);\n");
code.append("\t\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find reader for \" + manifest + \". Enable runtime conversion by initializing DslJson with new DslJson<>(Settings.basicSetup())\");\n");
code.append("\t\t\t\t}\n");
code.append("\t\t\t}\n");
code.append("\t\t\treturn reader_").append(attr.name).append(";\n");
Expand All @@ -154,14 +161,14 @@ private void asFormatConverter(final StructInfo si, final String name, final Str
code.append("\t\t\t\tjava.lang.reflect.Type manifest = ").append(type).append(";\n");
code.append("\t\t\t\twriter_").append(attr.name).append(" = __dsljson.tryFindWriter(manifest);\n");
code.append("\t\t\t\tif (writer_").append(attr.name).append(" == null) {\n");
code.append("\t\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find writer for \" + manifest);\n");
code.append("\t\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find writer for \" + manifest + \". Enable runtime conversion by initializing DslJson with new DslJson<>(Settings.basicSetup())\");\n");
code.append("\t\t\t\t}\n");
code.append("\t\t\t}\n");
code.append("\t\t\treturn writer_").append(attr.name).append(";\n");
code.append("\t\t}\n");
}
if (attr.isArray) {
content = extractRawType(((ArrayType) attr.type).getComponentType());
String content = Context.extractRawType(((ArrayType) attr.type).getComponentType());
code.append("\t\tprivate final ").append(content).append("[] emptyArray_").append(attr.name).append(";\n");
}
}
Expand All @@ -180,15 +187,41 @@ private void asFormatConverter(final StructInfo si, final String name, final Str
for (AttributeInfo attr : si.attributes.values()) {
String typeName = attr.type.toString();
boolean hasConverter = context.inlinedConverters.containsKey(typeName);
String content = attr.collectionContent(context.knownTypes);
List<String> types = attr.collectionContent(context.knownTypes);
StructInfo target = context.structs.get(attr.typeName);
if (attr.converter == null && (target == null || target.converter == null) && !hasConverter && !isStaticEnum(attr) && !attr.isJsonObject) {
if (target != null && attr.isEnum(context.structs)) {
code.append("\t\t\tthis.converter_").append(attr.name).append(" = new ").append(findConverterName(target)).append(".EnumConverter(__dsljson);\n");
} else if (content != null) {
String type = typeOrClass(nonGenericObject(content), content);
code.append("\t\t\tthis.reader_").append(attr.name).append(" = __dsljson.tryFindReader(").append(type).append(");\n");
code.append("\t\t\tthis.writer_").append(attr.name).append(" = __dsljson.tryFindWriter(").append(type).append(");\n");
} else if (types != null && types.size() == 1) {
String content = types.get(0);
OptimizedConverter converter = context.inlinedConverters.get(content);
if (converter != null) {
code.append("\t\t\tthis.reader_").append(attr.name).append(" = ").append(converter.decoderField).append(";\n");
code.append("\t\t\tthis.writer_").append(attr.name).append(" = ").append(converter.encoderField).append(";\n");
} else {
String type = typeOrClass(nonGenericObject(content), content);
code.append("\t\t\tthis.reader_").append(attr.name).append(" = __dsljson.tryFindReader(").append(type).append(");\n");
code.append("\t\t\tthis.writer_").append(attr.name).append(" = __dsljson.tryFindWriter(").append(type).append(");\n");
}
} else if (types != null && types.size() == 2) {
OptimizedConverter converterKey = context.inlinedConverters.get(types.get(0));
if (converterKey != null) {
code.append("\t\t\tthis.key_reader_").append(attr.name).append(" = ").append(converterKey.decoderField).append(";\n");
code.append("\t\t\tthis.key_writer_").append(attr.name).append(" = ").append(converterKey.encoderField).append(";\n");
} else {
String typeKey = typeOrClass(nonGenericObject(types.get(0)), types.get(0));
code.append("\t\t\tthis.key_reader_").append(attr.name).append(" = __dsljson.tryFindReader(").append(typeKey).append(");\n");
code.append("\t\t\tthis.key_writer_").append(attr.name).append(" = __dsljson.tryFindWriter(").append(typeKey).append(");\n");
}
OptimizedConverter converterValue = context.inlinedConverters.get(types.get(1));
if (converterValue != null) {
code.append("\t\t\tthis.value_reader_").append(attr.name).append(" = ").append(converterValue.decoderField).append(";\n");
code.append("\t\t\tthis.value_writer_").append(attr.name).append(" = ").append(converterValue.encoderField).append(";\n");
} else {
String typeValue = typeOrClass(nonGenericObject(types.get(1)), types.get(1));
code.append("\t\t\tthis.value_reader_").append(attr.name).append(" = __dsljson.tryFindReader(").append(typeValue).append(");\n");
code.append("\t\t\tthis.value_writer_").append(attr.name).append(" = __dsljson.tryFindWriter(").append(typeValue).append(");\n");
}
} else if (attr.isGeneric && !attr.containsStructOwnerType) {
String type;
if (attr.isArray) {
Expand All @@ -200,23 +233,25 @@ private void asFormatConverter(final StructInfo si, final String name, final Str
code.append("\t\t\tjava.lang.reflect.Type manifest_").append(attr.name).append(" = ").append(type).append(";\n");
code.append("\t\t\tthis.reader_").append(attr.name).append(" = __dsljson.tryFindReader(manifest_").append(attr.name).append(");\n");
code.append("\t\t\tif (reader_").append(attr.name).append(" == null) {\n");
code.append("\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find reader for \" + manifest_").append(attr.name).append(");\n");
code.append("\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find reader for \" + manifest_").append(attr.name);
code.append(" + \". Enable runtime conversion by initializing DslJson with new DslJson<>(Settings.withRuntime().includeServiceLoader())\");\n");
code.append("\t\t\t}\n");

code.append("\t\t\tthis.writer_").append(attr.name).append(" = __dsljson.tryFindWriter(manifest_").append(attr.name).append(");\n");
code.append("\t\t\tif (writer_").append(attr.name).append(" == null) {\n");
code.append("\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find writer for \" + manifest_").append(attr.name).append(");\n");
code.append("\t\t\t\tthrow new com.dslplatform.json.SerializationException(\"Unable to find writer for \" + manifest_").append(attr.name);
code.append(" + \". Enable runtime conversion by initializing DslJson with new DslJson<>(Settings.withRuntime().includeServiceLoader())\");\n");
code.append("\t\t\t}\n");
}
if (attr.isArray) {
TypeMirror arrayComponentType = ((ArrayType) attr.type).getComponentType();
if (arrayComponentType.getKind() == TypeKind.TYPEVAR) {
code.append("\t\t\tthis.emptyArray_").append(attr.name).append(" = ");
content = arrayComponentType.toString();
String content = arrayComponentType.toString();
code.append("(").append(content).append("[]) java.lang.reflect.Array.newInstance((Class<?>) actualTypes[");
code.append(attr.typeVariablesIndex.get(content).toString()).append("], 0);\n");
} else {
content = extractRawType(arrayComponentType);
String content = Context.extractRawType(arrayComponentType);
code.append("\t\t\tthis.emptyArray_").append(attr.name).append(" = new ").append(content).append("[0];\n");
}
}
Expand Down Expand Up @@ -615,7 +650,12 @@ private void writeProperty(AttributeInfo attr, boolean checkedDefault) throws IO
} else if (target != null && attr.isEnum(context.structs)) {
enumTemplate.writeName(code, target, readValue, "converter_" + attr.name);
} else if (attr.collectionContent(context.knownTypes) != null) {
code.append("writer.serialize(").append(readValue).append(", writer_").append(attr.name).append(");\n");
code.append("writer.serialize(").append(readValue);
if (attr.isMap) {
code.append(", key_writer_").append(attr.name).append(", value_writer_").append(attr.name).append(");\n");
} else {
code.append(", writer_").append(attr.name).append(");\n");
}
} else if (attr.isGeneric && !attr.containsStructOwnerType) {
if (attr.isArray) {
code.append("writer.serialize(").append(readValue).append(", writer_").append(attr.name).append(");\n");
Expand Down Expand Up @@ -713,16 +753,10 @@ private void setPropertyValue(AttributeInfo attr, String alignment) throws IOExc
code.append("converter_").append(attr.name).append(".read(reader)");
}
} else if (attr.collectionContent(context.knownTypes) != null) {
if (attr.isArray) {
String content = extractRawType(((ArrayType) attr.type).getComponentType());
code.append("(").append(content).append("[])reader.readArray(reader_").append(attr.name);
code.append(", emptyArray_").append(attr.name).append(")");
} else {
code.append("reader.readCollection(reader_").append(attr.name).append(")");
}
context.serializeKnownCollection(attr);
} else if (attr.isGeneric && !attr.containsStructOwnerType) {
if (attr.isArray) {
String content = extractRawType(((ArrayType) attr.type).getComponentType());
String content = Context.extractRawType(((ArrayType) attr.type).getComponentType());
code.append("(").append(content).append("[])reader.readArray(reader_").append(attr.name);
code.append(", emptyArray_").append(attr.name).append(")");
} else {
Expand Down Expand Up @@ -771,13 +805,8 @@ private void readPropertyValue(AttributeInfo attr, String alignment) throws IOEx
code.append("converter_").append(attr.name).append(".read(reader)");
}
} else if (attr.collectionContent(context.knownTypes) != null) {
if (attr.isArray) {
String content = extractRawType(((ArrayType) attr.type).getComponentType());
code.append("(").append(content).append("[])reader.readArray(reader_").append(attr.name);
code.append(", emptyArray_").append(attr.name).append(");\n");
} else {
code.append("reader.readCollection(reader_").append(attr.name).append(");\n");
}
context.serializeKnownCollection(attr);
code.append(";\n");
} else if (attr.isGeneric && !attr.containsStructOwnerType) {
code.append("reader_").append(attr.name).append(".read(reader);\n");
} else {
Expand All @@ -793,12 +822,4 @@ private static int calcWeakHash(String name) {
}
return hash;
}

private static String extractRawType(TypeMirror type) {
if (type.getKind() == TypeKind.DECLARED) {
return ((DeclaredType) type).asElement().toString();
} else {
return type.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
import com.dslplatform.json.Nullable;

final class OptimizedConverter {
private final String encoderField;
public final String encoderField;
private final String nonNullableEncoderMethod;
private final String decoderField;
public final String decoderField;
private final String nonNullableDecoderMethod;
final String defaultValue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public abstract class ArrayAnalyzer {
public static class Runtime {
public static final JsonReader.ReadObject<Object[]> JSON_READER = new JsonReader.ReadObject<Object[]>() {
@Override
public Object[] read(JsonReader r) throws IOException {
if (r.wasNull()) return null;
return ObjectConverter.deserializeList(r).toArray();
public Object[] read(JsonReader reader) throws IOException {
if (reader.wasNull()) return null;
return ObjectConverter.deserializeList(reader).toArray();
}
};
public static final JsonWriter.WriteObject<Object[]> JSON_WRITER = new JsonWriter.WriteObject<Object[]>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public abstract class CollectionAnalyzer {
public static class Runtime {
public static final JsonReader.ReadObject<List<Object>> JSON_READER = new JsonReader.ReadObject<List<Object>>() {
@Override
public List<Object> read(JsonReader r) throws IOException {
if (r.wasNull()) return null;
return ObjectConverter.deserializeList(r);
public List<Object> read(JsonReader reader) throws IOException {
if (reader.wasNull()) return null;
return ObjectConverter.deserializeList(reader);
}
};
public static final JsonWriter.WriteObject<List<Object>> JSON_WRITER = new JsonWriter.WriteObject<List<Object>>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,11 +232,14 @@ private static <T> ImmutableDescription<T> analyze(final Type manifest, final Cl
final ImmutableDescription<T> converter = new ImmutableDescription<>(
manifest,
defArgs,
args -> {
try {
return raw.cast(ctor.newInstance(args));
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
new Settings.Function<Object[], T>() {
@Override
public T apply(@Nullable Object[] args) {
try {
return raw.cast(ctor.newInstance(args));
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
},
writeProps,
Expand Down
21 changes: 17 additions & 4 deletions java8/src/main/java/com/dslplatform/json/runtime/MapAnalyzer.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package com.dslplatform.json.runtime;

import com.dslplatform.json.DslJson;
import com.dslplatform.json.JsonReader;
import com.dslplatform.json.JsonWriter;
import com.dslplatform.json.Nullable;
import com.dslplatform.json.*;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
Expand All @@ -13,6 +10,22 @@

public abstract class MapAnalyzer {

public static class Runtime {
public static final JsonReader.ReadObject<Map<String, Object>> JSON_READER = new JsonReader.ReadObject<Map<String, Object>>() {
@Override
public Map<String, Object> read(JsonReader reader) throws IOException {
if (reader.wasNull()) return null;
return ObjectConverter.deserializeMap(reader);
}
};
public static final JsonWriter.WriteObject<Map<String, Object>> JSON_WRITER = new JsonWriter.WriteObject<Map<String, Object>>() {
@Override
public void write(JsonWriter writer, @Nullable Map<String, Object> value) {
ObjectConverter.serializeNullableMap(value, writer);
}
};
}

private static final JsonReader.ReadObject<String> stringReader = new JsonReader.ReadObject<String>() {
@Nullable
@Override
Expand Down
Loading

0 comments on commit 2f07232

Please sign in to comment.