diff --git a/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/GProcessor.java b/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/GProcessor.java index 2ea88f9..78f2cda 100644 --- a/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/GProcessor.java +++ b/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/GProcessor.java @@ -11,6 +11,8 @@ import io.gitlab.jfronny.commons.serialize.generator.core.value.ValueCreator; import javax.annotation.processing.Messager; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.type.TypeMirror; import java.io.*; @@ -19,8 +21,10 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.UnaryOperator; +import java.util.stream.Stream; public abstract class GProcessor { protected final ValueCreator valueCreator; @@ -69,13 +73,26 @@ public abstract class GProcessor { return null; } - protected String getSerializedName(Property property) { + protected List getSerializedNames(Property property) { for (AnnotationMirror annotationMirror : property.getAnnotations()) { if (annotationMirror.getAnnotationType().asElement().toString().equals(Cl.SERIALIZED_NAME.toString())) { - return (String) annotationMirror.getElementValues().values().iterator().next().getValue(); + String value = null; + List alternate = null; + for (Map.Entry entry : annotationMirror.getElementValues().entrySet()) { + switch (entry.getKey().getSimpleName().toString()) { + case "value" -> value = (String) entry.getValue().getValue(); + case "alternate" -> alternate = ((List) entry.getValue().getValue()).stream() + .map(s -> (AnnotationValue) s) + .map(s -> (String) s.getValue()) + .toList(); + } + } + if (value == null) value = property.getName(); + if (alternate == null) return List.of(value); + return Stream.concat(Stream.of(value), alternate.stream()).toList(); } } - return property.getName(); + return List.of(property.getName()); } protected MethodSpec.Builder extension(MethodSpec.Builder method) { diff --git a/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/InstanceProcessor.java b/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/InstanceProcessor.java index 1d65ff0..6fee779 100644 --- a/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/InstanceProcessor.java +++ b/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/InstanceProcessor.java @@ -81,12 +81,12 @@ public class InstanceProcessor extends GProcessor { Runnable writeGet = () -> code.add("value.$N", param.getCallableName()); if (param.getType().getKind().isPrimitive()) { generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, writeGet); } else { code.beginControlFlow("if (value.$N != null || writer.isSerializeNulls())", param.getCallableName()); generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); code.addStatement("if (value.$N == null) writer.nullValue()", param.getCallableName()); code.beginControlFlow("else"); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, writeGet); @@ -98,13 +98,13 @@ public class InstanceProcessor extends GProcessor { if (isIgnored(param)) continue; if (param.getType().getKind().isPrimitive()) { generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, () -> code.add("value.$N()", param.getCallableName())); } else { code.addStatement("$T $L$N = value.$N()", param.getType(), "$", param.getCallableName(), param.getCallableName()); code.beginControlFlow("if ($L$N != null || writer.isSerializeNulls())", "$", param.getCallableName()); generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); code.addStatement("if ($L$N == null) writer.nullValue()", "$", param.getCallableName()); code.beginControlFlow("else"); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, () -> code.add("$L$N", "$", param.getCallableName())); @@ -148,24 +148,30 @@ public class InstanceProcessor extends GProcessor { .beginControlFlow("switch (reader.nextName())"); for (Property param : properties.names) { if (isIgnored(param) && getAlternative(properties, param) == null) continue; - code.beginControlFlow("case $S ->", getSerializedName(param)); - if (param.getType().getKind().isPrimitive()) { - code.add("_$N = ", param.getName()); - Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); - code.add(";\n"); - } else { - code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN) - .addStatement("reader.nextNull()") - .addStatement("_$N = null", param.getName()); - code.unindent().add("} else _$N = ", param.getName()); - Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); - code.add(";\n"); + List names = getSerializedNames(param); + for (String serializedName : names) { + code.beginControlFlow("case $S ->", serializedName); + code.addStatement("if (has_$N && !reader.isLenient()) throw new $T($S)", param.getName(), Cl.MALFORMED_DATA_EXCEPTION, "Duplicate entry for: " + names.getFirst()); + if (param.getType().getKind().isPrimitive()) { + code.add("_$N = ", param.getName()); + Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); + code.add(";\n"); + } else { + code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN) + .addStatement("reader.nextNull()") + .addStatement("_$N = null", param.getName()); + code.unindent().add("} else _$N = ", param.getName()); + Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); + code.add(";\n"); + } + code.addStatement("has_$N = true", param.getName()); + code.endControlFlow(); } - code.addStatement("has_$N = true", param.getName()); - code.endControlFlow(); } - code.add("default -> ") - .addStatement("reader.skipValue()"); + code.beginControlFlow("case String s -> ") + .addStatement("if (reader.isLenient()) reader.skipValue()") + .addStatement("else throw new $T($S + s)", Cl.MALFORMED_DATA_EXCEPTION, "Unexpected name: ") + .endControlFlow(); code.endControlFlow() .endControlFlow() diff --git a/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/StaticProcessor.java b/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/StaticProcessor.java index 3bdaa4e..5f2b53f 100644 --- a/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/StaticProcessor.java +++ b/commons-serialize-generator/src/main/java/io/gitlab/jfronny/commons/serialize/generator/gprocessor/StaticProcessor.java @@ -46,12 +46,12 @@ public class StaticProcessor extends GProcessor { Runnable writeGet = () -> code.add("$T.$N", self.getTypeName(), param.getCallableName()); if (param.getType().getKind().isPrimitive()) { generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, writeGet); } else { code.beginControlFlow("if ($T.$N != null || writer.isSerializeNulls())", self.getTypeName(), param.getCallableName()); generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); code.addStatement("if ($T.$N == null) writer.nullValue()", self.getTypeName(), param.getCallableName()); code.beginControlFlow("else"); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, writeGet); @@ -63,13 +63,13 @@ public class StaticProcessor extends GProcessor { if (isIgnored(param)) continue; if (param.getType().getKind().isPrimitive()) { generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, () -> code.add("$T.$N()", self.getTypeName(), param.getCallableName())); } else { code.addStatement("$T $L$N = $T.$N()", param.getType(), "$", param.getCallableName(), self.getTypeName(), param.getCallableName()); code.beginControlFlow("if ($L$N != null || writer.isSerializeNulls())", "$", param.getCallableName()); generateComments(param, code); - code.addStatement("writer.name($S)", getSerializedName(param)); + code.addStatement("writer.name($S)", getSerializedNames(param).getFirst()); code.addStatement("if ($L$N == null) writer.nullValue()", "$", param.getCallableName()); code.beginControlFlow("else"); Adapters.generateWrite(param, spec, code, typeVariables, otherAdapters, refs, message, () -> code.add("$L$N", "$", param.getCallableName())); @@ -113,8 +113,8 @@ public class StaticProcessor extends GProcessor { code.addStatement("$T.$N = $L", self.getTypeName(), param.getName(), TypeHelper.getDefaultValue(param.getType())); } else { code.addStatement("$T _$N = $L", param.getType(), param.getName(), TypeHelper.getDefaultValue(param.getType())); - code.addStatement("boolean has_$N = false", param.getName()); } + code.addStatement("boolean has_$N = false", param.getName()); } if (isEmpty) { code.addStatement("reader.skipValue()"); @@ -127,30 +127,36 @@ public class StaticProcessor extends GProcessor { param = altMap.get(param); if (param == null) continue; } - code.beginControlFlow("case $S ->", getSerializedName(param)); - if (param.getType().getKind().isPrimitive()) { - if (param instanceof Property.Field) code.add("$T.$N = ", self.getTypeName(), param.getName()); - else code.add("_$N = ", param.getName()); - Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); - code.add(";\n"); - } else { - code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN) - .addStatement("reader.nextNull()"); - if (param instanceof Property.Field) { - code.addStatement("$T.$N = null", self.getTypeName(), param.getName()); - code.unindent().add("} else $T.$N = ", self.getTypeName(), param.getName()); + List names = getSerializedNames(param); + for (String serializedName : names) { + code.beginControlFlow("case $S ->", serializedName); + code.addStatement("if (has_$N && !reader.isLenient()) throw new $T($S)", param.getName(), Cl.MALFORMED_DATA_EXCEPTION, "Duplicate entry for: " + names.getFirst()); + if (param.getType().getKind().isPrimitive()) { + if (param instanceof Property.Field) code.add("$T.$N = ", self.getTypeName(), param.getName()); + else code.add("_$N = ", param.getName()); + Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); + code.add(";\n"); } else { - code.addStatement("_$N = null", param.getName()); - code.unindent().add("} else _$N = ", param.getName()); + code.beginControlFlow("if (reader.peek() == $T.NULL)", Cl.GSON_TOKEN) + .addStatement("reader.nextNull()"); + if (param instanceof Property.Field) { + code.addStatement("$T.$N = null", self.getTypeName(), param.getName()); + code.unindent().add("} else $T.$N = ", self.getTypeName(), param.getName()); + } else { + code.addStatement("_$N = null", param.getName()); + code.unindent().add("} else _$N = ", param.getName()); + } + Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); + code.add(";\n"); } - Adapters.generateRead(param, spec, code, typeVariables, otherAdapters, refs, message); - code.add(";\n"); + code.addStatement("has_$N = true", param.getName()); + code.endControlFlow(); } - if (!(param instanceof Property.Field)) code.addStatement("has_$N = true", param.getName()); - code.endControlFlow(); } - code.add("default -> ") - .addStatement("reader.skipValue()"); + code.beginControlFlow("case String s -> ") + .addStatement("if (reader.isLenient()) reader.skipValue()") + .addStatement("else throw new $T($S + s)", Cl.MALFORMED_DATA_EXCEPTION, "Unexpected name: ") + .endControlFlow(); code.endControlFlow() .endControlFlow() diff --git a/commons-serialize-generator/src/test/java/io/gitlab/jfronny/commons/serialize/generator/test/Main.java b/commons-serialize-generator/src/test/java/io/gitlab/jfronny/commons/serialize/generator/test/Main.java index 54cf4c8..363f8d8 100644 --- a/commons-serialize-generator/src/test/java/io/gitlab/jfronny/commons/serialize/generator/test/Main.java +++ b/commons-serialize-generator/src/test/java/io/gitlab/jfronny/commons/serialize/generator/test/Main.java @@ -24,7 +24,7 @@ public class Main { public Boolean someBool; @GComment("Halal") - @SerializedName("bingChiller") + @SerializedName(value = "bingChiller", alternate = {"bingChiller2", "bingChiller3"}) public String getBass() { return "Yes"; }