Skip to content
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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Jappe van der Hel <[email protected]>
John Paul Taylor II <[email protected]>
Karthik kathari <[email protected]>
Kevin Chirls <[email protected]>
Lars Uffmann <[email protected]>
Liu DongMiao <[email protected]>
Liam Pace <[email protected]>
Luan Nico <[email protected]>
Expand Down
10 changes: 10 additions & 0 deletions src/core/lombok/ConfigurationKeys.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import lombok.core.configuration.ConfigurationKey;
import lombok.core.configuration.FlagUsageType;
import lombok.core.configuration.IdentifierName;
import lombok.core.configuration.JacksonVersion;
import lombok.core.configuration.LogDeclaration;
import lombok.core.configuration.NullAnnotationLibrary;
import lombok.core.configuration.NullCheckExceptionType;
Expand Down Expand Up @@ -711,6 +712,15 @@ private ConfigurationKeys() {}
*/
public static final ConfigurationKey<FlagUsageType> JACKSONIZED_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.jacksonized.flagUsage", "Emit a warning or error if @Jacksonized is used.") {};


/**
* lombok configuration: {@code lombok.jacksonized.jacksonVersion} = {@code 2} | {@code 3} | {@code 2_3}.
*
* The jackson major version to use.
*/
public static final ConfigurationKey<JacksonVersion> JACKSONIZED_JACKSON_VERSION = new ConfigurationKey<JacksonVersion>("lombok.jacksonized.jacksonVersion", "Select the jackson major version to use (default: 2)") {};


// ----- Configuration System -----

/**
Expand Down
54 changes: 54 additions & 0 deletions src/core/lombok/core/configuration/JacksonVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package lombok.core.configuration;

public final class JacksonVersion implements ConfigurationValueType {

private final boolean useJackson2;
private final boolean useJackson3;

private static final JacksonVersion _2 = new JacksonVersion(true, false);
private static final JacksonVersion _3 = new JacksonVersion(false, true);
private static final JacksonVersion _23 = new JacksonVersion(true, true);

public static final JacksonVersion getDefault() {
return _2;
}

public static String exampleValue() {
return "2, 3 or 2_3";
}

public static String description() {
return "Select the major version of the jackson framework.";
}

public static JacksonVersion valueOf(String value) {
if (value == null || value.isEmpty()) {
return getDefault();
} else if ("2".equals(value.trim())) {
return _2;
} else if ("3".equals(value.trim())) {
return _3;
} else if ("2_3".equals(value.trim())) {
return _23;
} else {
throw new IllegalArgumentException("Unsupported Jackson version selector.");
}
}

private JacksonVersion(boolean useJackson2, boolean useJackson3) {
this.useJackson2 = useJackson2;
this.useJackson3 = useJackson3;
}

public boolean useJackson2() {
return useJackson2;
}

public boolean useJackson3() {
return useJackson3;
}

public boolean isValid() {
return useJackson2 || useJackson3;
}
}
2 changes: 2 additions & 0 deletions src/core/lombok/core/handlers/HandlerUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,7 @@ public static int primeForNull() {
"com.fasterxml.jackson.annotation.JsonUnwrapped",
"com.fasterxml.jackson.annotation.JsonView",
"com.fasterxml.jackson.databind.annotation.JsonDeserialize",
"tools.jackson.databind.annotation.JsonDeserialize",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText",
Expand All @@ -488,6 +489,7 @@ public static int primeForNull() {
"com.fasterxml.jackson.annotation.JsonTypeName",
"com.fasterxml.jackson.annotation.JsonView",
"com.fasterxml.jackson.databind.annotation.JsonNaming",
"tools.jackson.databind.annotation.JsonNaming",
}));
}

Expand Down
34 changes: 28 additions & 6 deletions src/core/lombok/eclipse/handlers/HandleJacksonized.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.AST.Kind;
import lombok.core.configuration.JacksonVersion;
import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
Expand All @@ -62,8 +63,10 @@
@HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated).
public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {

private static final char[][] JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder");
private static final char[][] JSON_DESERIALIZE_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonDeserialize");
private static final char[][] JACKSON3_JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("tools.jackson.databind.annotation.JsonPOJOBuilder");
private static final char[][] JACKSON2_JSON_POJO_BUILDER_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder");
private static final char[][] JACKSON3_JSON_DESERIALIZE_ANNOTATION = Eclipse.fromQualifiedName("tools.jackson.databind.annotation.JsonDeserialize");
private static final char[][] JACKSON2_JSON_DESERIALIZE_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.databind.annotation.JsonDeserialize");
private static final char[][] JSON_PROPERTY_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.annotation.JsonProperty");

@Override public void handle(AnnotationValues<Jacksonized> annotation, Annotation ast, EclipseNode annotationNode) {
Expand Down Expand Up @@ -136,12 +139,27 @@ private void handleJacksonizedBuilder(Annotation ast, EclipseNode annotationNode
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
if (hasAnnotation("tools.jackson.databind.annotation.JsonDeserialize", tdNode)) {
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
long p = (long) ast.sourceStart << 32 | ast.sourceEnd;
TypeReference builderClassExpression = namePlusTypeParamsToTypeReference(builderClassNode, null, p);
ClassLiteralAccess builderClassLiteralAccess = new ClassLiteralAccess(td.sourceEnd, builderClassExpression);
MemberValuePair builderMvp = new MemberValuePair("builder".toCharArray(), td.sourceStart, td.sourceEnd, builderClassLiteralAccess);
td.annotations = addAnnotation(td, td.annotations, JSON_DESERIALIZE_ANNOTATION, builderMvp);


JacksonVersion jacksonVersion = annotationNode.getAst().readConfigurationOr(ConfigurationKeys.JACKSONIZED_JACKSON_VERSION, JacksonVersion.getDefault());
if (jacksonVersion == null || !jacksonVersion.isValid()) {
annotationNode.addError("No valid jackson version selected.");
return;
}

if (jacksonVersion.useJackson2()) {
td.annotations = addAnnotation(td, td.annotations, JACKSON2_JSON_DESERIALIZE_ANNOTATION, builderMvp);
}
if (jacksonVersion.useJackson3()) {
td.annotations = addAnnotation(td, td.annotations, JACKSON3_JSON_DESERIALIZE_ANNOTATION, builderMvp);
}
// Copy annotations from the class to the builder class.
Annotation[] copyableAnnotations = findJacksonAnnotationsOnClass(td, tdNode);
builderClass.annotations = copyAnnotations(builderClass, builderClass.annotations, copyableAnnotations);
Expand All @@ -151,8 +169,12 @@ private void handleJacksonizedBuilder(Annotation ast, EclipseNode annotationNode
MemberValuePair withPrefixMvp = new MemberValuePair("withPrefix".toCharArray(), builderClass.sourceStart, builderClass.sourceEnd, withPrefixLiteral);
StringLiteral buildMethodNameLiteral = new StringLiteral(buildMethodName.toCharArray(), builderClass.sourceStart, builderClass.sourceEnd, 0);
MemberValuePair buildMethodNameMvp = new MemberValuePair("buildMethodName".toCharArray(), builderClass.sourceStart, builderClass.sourceEnd, buildMethodNameLiteral);
builderClass.annotations = addAnnotation(builderClass, builderClass.annotations, JSON_POJO_BUILDER_ANNOTATION, withPrefixMvp, buildMethodNameMvp);

if (jacksonVersion.useJackson2()) {
builderClass.annotations = addAnnotation(builderClass, builderClass.annotations, JACKSON2_JSON_POJO_BUILDER_ANNOTATION, withPrefixMvp, buildMethodNameMvp);
}
if (jacksonVersion.useJackson3()) {
builderClass.annotations = addAnnotation(builderClass, builderClass.annotations, JACKSON3_JSON_POJO_BUILDER_ANNOTATION, withPrefixMvp, buildMethodNameMvp);
}
// @SuperBuilder? Make it package-private!
if (superBuilderAnnotationNode != null)
builderClass.modifiers = builderClass.modifiers & ~ClassFileConstants.AccPrivate;
Expand Down
57 changes: 44 additions & 13 deletions src/core/lombok/javac/handlers/HandleJacksonized.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.configuration.JacksonVersion;
import lombok.core.handlers.HandlerUtil;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
Expand Down Expand Up @@ -168,13 +169,24 @@ private void handleJacksonizedBuilder(JavacNode annotationNode, JavacNode annota
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}
JCExpression jsonDeserializeType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonDeserialize");
JCExpression builderClassExpression = namePlusTypeParamsToTypeReference(maker, tdNode, annotationNode.toName(builderClassName), false, List.<JCTypeParameter>nil());
JCFieldAccess builderClassReference = maker.Select(builderClassExpression, annotatedNode.toName("class"));
JCExpression assign = maker.Assign(maker.Ident(annotationNode.toName("builder")), builderClassReference);
JCAnnotation annotationJsonDeserialize = maker.Annotation(jsonDeserializeType, List.of(assign));
recursiveSetGeneratedBy(annotationJsonDeserialize, annotationNode);
td.mods.annotations = td.mods.annotations.append(annotationJsonDeserialize);
if (hasAnnotation("tools.jackson.databind.annotation.JsonDeserialize", tdNode)) {
annotationNode.addError("@JsonDeserialize already exists on class. Either delete @JsonDeserialize, or remove @Jacksonized and manually configure Jackson.");
return;
}

JacksonVersion jacksonVersion = annotationNode.getAst().readConfigurationOr(ConfigurationKeys.JACKSONIZED_JACKSON_VERSION, JacksonVersion.getDefault());
if (jacksonVersion == null || !jacksonVersion.isValid()) {
annotationNode.addError("No valid jackson version selected.");
return;
}
if (jacksonVersion.useJackson2()) {
JCExpression jsonDeserializeType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonDeserialize");
insertJsonDeserializeAnnotation(annotationNode, annotatedNode, tdNode, td, maker, builderClassName, jsonDeserializeType);
}
if (jacksonVersion.useJackson3()) {
JCExpression jsonDeserializeType = chainDots(annotatedNode, "tools", "jackson", "databind", "annotation", "JsonDeserialize");
insertJsonDeserializeAnnotation(annotationNode, annotatedNode, tdNode, td, maker, builderClassName, jsonDeserializeType);
}

// Copy annotations from the class to the builder class.
List<JCAnnotation> copyableAnnotations = findJacksonAnnotationsOnClass(tdNode);
Expand All @@ -183,17 +195,36 @@ private void handleJacksonizedBuilder(JavacNode annotationNode, JavacNode annota
recursiveSetGeneratedBy(anno, annotationNode);
}
builderClass.mods.annotations = builderClass.mods.annotations.appendList(copiedAnnotations);

// Insert @JsonPOJOBuilder on the builder class.
JCExpression jsonPOJOBuilderType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonPOJOBuilder");

if (jacksonVersion.useJackson2()) {
JCExpression jsonPOJOBuilderType = chainDots(annotatedNode, "com", "fasterxml", "jackson", "databind", "annotation", "JsonPOJOBuilder");
insertJsonPojoAnnotation(annotationNode, annotatedNode, setPrefix, buildMethodName, maker, builderClass, jsonPOJOBuilderType);
}
if (jacksonVersion.useJackson3()) {
JCExpression jsonPOJOBuilderType = chainDots(annotatedNode, "tools", "jackson", "databind", "annotation", "JsonPOJOBuilder");
insertJsonPojoAnnotation(annotationNode, annotatedNode, setPrefix, buildMethodName, maker, builderClass, jsonPOJOBuilderType);
}
// @SuperBuilder? Make it package-private!
if (superBuilderAnnotationNode != null) builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE;
}

// Insert @JsonPOJOBuilder on the builder class.
private void insertJsonPojoAnnotation(JavacNode annotationNode, JavacNode annotatedNode, String setPrefix, String buildMethodName, JavacTreeMaker maker, JCClassDecl builderClass, JCExpression jsonPOJOBuilderType) {
JCExpression withPrefixExpr = maker.Assign(maker.Ident(annotationNode.toName("withPrefix")), maker.Literal(setPrefix));
JCExpression buildMethodNameExpr = maker.Assign(maker.Ident(annotationNode.toName("buildMethodName")), maker.Literal(buildMethodName));
JCAnnotation annotationJsonPOJOBuilder = maker.Annotation(jsonPOJOBuilderType, List.of(withPrefixExpr, buildMethodNameExpr));
recursiveSetGeneratedBy(annotationJsonPOJOBuilder, annotatedNode);
builderClass.mods.annotations = builderClass.mods.annotations.append(annotationJsonPOJOBuilder);

// @SuperBuilder? Make it package-private!
if (superBuilderAnnotationNode != null) builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE;
}

// Insert @JsonDeserialize on the class.
private void insertJsonDeserializeAnnotation(JavacNode annotationNode, JavacNode annotatedNode, JavacNode tdNode, JCClassDecl td, JavacTreeMaker maker, String builderClassName, JCExpression jsonDeserializeType) {
JCExpression builderClassExpression = namePlusTypeParamsToTypeReference(maker, tdNode, annotationNode.toName(builderClassName), false, List.<JCTypeParameter>nil());
JCFieldAccess builderClassReference = maker.Select(builderClassExpression, annotatedNode.toName("class"));
JCExpression assign = maker.Assign(maker.Ident(annotationNode.toName("builder")), builderClassReference);
JCAnnotation annotationJsonDeserialize = maker.Annotation(jsonDeserializeType, List.of(assign));
recursiveSetGeneratedBy(annotationJsonDeserialize, annotationNode);
td.mods.annotations = td.mods.annotations.append(annotationJsonDeserialize);
}

private String getBuilderClassName(JavacNode annotationNode, JavacNode annotatedNode, JCClassDecl td, AnnotationValues<Builder> builderAnnotation, JavacTreeMaker maker) {
Expand Down
12 changes: 12 additions & 0 deletions test/stubs/tools/jackson/databind/annotation/JsonDeserialize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package tools.jackson.databind.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonDeserialize {
public Class<?> builder() default Void.class;
}
10 changes: 10 additions & 0 deletions test/stubs/tools/jackson/databind/annotation/JsonPOJOBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package tools.jackson.databind.annotation;

import java.lang.annotation.*;

@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonPOJOBuilder {
public String buildMethodName() default "build";
public String withPrefix() default "with";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
//CONF: lombok.builder.className = Test*Name
import java.util.List;
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderComplex.TestVoidName.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderComplex.TestVoidName.class)
class JacksonizedBuilderComplex {
private static <T extends Number> void testVoidWithGenerics(T number, int arg2, String arg3, JacksonizedBuilderComplex selfRef) {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "with", buildMethodName = "execute")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "with", buildMethodName = "execute")
public static class TestVoidName<T extends Number> {
@java.lang.SuppressWarnings("all")
@lombok.Generated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSimple.JacksonizedBuilderSimpleBuilder.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSimple.JacksonizedBuilderSimpleBuilder.class)
class JacksonizedBuilderSimple<T> {
private final int noshow = 0;
private final int yes;
Expand All @@ -18,6 +19,7 @@ class JacksonizedBuilderSimple<T> {
@lombok.Generated
@JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
protected static class JacksonizedBuilderSimpleBuilder<T> {
@java.lang.SuppressWarnings("all")
@lombok.Generated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder.class)
public class JacksonizedBuilderSingular {
@JsonAnySetter
private Map<String, Object> any;
Expand All @@ -26,6 +27,7 @@ public class JacksonizedBuilderSingular {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class JacksonizedBuilderSingularBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedOnRecord.JacksonizedOnRecordBuilder.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedOnRecord.JacksonizedOnRecordBuilder.class)
public record JacksonizedOnRecord(@JsonProperty("test") @Nullable String string, @JsonAnySetter List<String> values) {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@JsonIgnoreProperties
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class JacksonizedOnRecordBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
public class JacksonizedSuperBuilderSimple {
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedSuperBuilderSimple.Parent.ParentBuilderImpl.class)
@tools.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedSuperBuilderSimple.Parent.ParentBuilderImpl.class)
public static class Parent {
int field1;
@java.lang.SuppressWarnings("all")
Expand Down Expand Up @@ -36,6 +37,7 @@ public java.lang.String toString() {
@lombok.Generated
@com.fasterxml.jackson.annotation.JsonIgnoreProperties(ignoreUnknown = true)
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
@tools.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
static final class ParentBuilderImpl extends JacksonizedSuperBuilderSimple.Parent.ParentBuilder<JacksonizedSuperBuilderSimple.Parent, JacksonizedSuperBuilderSimple.Parent.ParentBuilderImpl> {
@java.lang.SuppressWarnings("all")
@lombok.Generated
Expand Down
Loading