ecj: Jacksonized for accessors [WIP]

This commit is contained in:
Jan Rieke committed 2025-03-27 14:06:27 +01:00
1 parent ef4afa4957
commit 70dd3f0940
14 files changed
+88 -25

No files matched your search

+1 -1
View File
@@ -747,7 +747,7 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.copyJacksonAnnotationsToSetters} = {@code true} | {@code false}.
*
* If <code>true</code>, copy certain Jackson annotations from a field to its corresponding getter. This was the behavior from lombok 1.18.16 to 1.18.34.
* If <code>true</code>, copy certain Jackson annotations from a field to its corresponding getter. This was the behavior from lombok 1.18.16 to 1.18.36.
* However, it turned out to be harmful in certain situations. Thus, the default is now <code>false</code>.
*/
public static final ConfigurationKey<Boolean> COPY_JACKSON_ANNOTATIONS_TO_SETTERS = new ConfigurationKey<Boolean>("lombok.copyJacksonAnnotationsToSetters", "Copy these annotations to getters, setters, with methods, builder-setters, etc.") {};
@@ -828,8 +828,14 @@ public class EclipseHandlerUtil {
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the setter.
*
* @param forceCopy If <code>true</code>, always copy the annotations regardless of the lombok configuration.
*/
public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node) {
public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node, boolean forceCopy) {
if (!forceCopy) {
Boolean copyAnnotations = node.getAst().readConfiguration(ConfigurationKeys.COPY_JACKSON_ANNOTATIONS_TO_SETTERS);
if (copyAnnotations == null || !copyAnnotations) return EMPTY_ANNOTATIONS_ARRAY;
}
return findAnnotationsInList(node, JACKSON_COPY_TO_SETTER_ANNOTATIONS);
}
@@ -1047,7 +1047,7 @@ public class HandleBuilder extends EclipseAnnotationHandler<Builder> {
}
List<Annotation> methodAnnsList = Collections.<Annotation>emptyList();
Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(bfd.originalFieldNode);
Annotation[] methodAnns = EclipseHandlerUtil.findCopyableToSetterAnnotations(bfd.originalFieldNode, true);
if (methodAnns != null && methodAnns.length > 0) methodAnnsList = Arrays.asList(methodAnns);
ASTNode source = job.sourceNode.get();
MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, toEclipseModifier(job.accessInners),
@@ -31,6 +31,7 @@ import java.util.List;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.StringLiteral;
@@ -47,6 +48,7 @@ import lombok.core.handlers.HandlerUtil;
import lombok.eclipse.Eclipse;
import lombok.eclipse.EclipseAnnotationHandler;
import lombok.eclipse.EclipseNode;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
import lombok.spi.Provides;
@@ -62,6 +64,7 @@ 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[][] JSON_PROPERTY_ANNOTATION = Eclipse.fromQualifiedName("com.fasterxml.jackson.annotation.JsonProperty");
@Override public void handle(AnnotationValues<Jacksonized> annotation, Annotation ast, EclipseNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.JACKSONIZED_FLAG_USAGE, "@Jacksonized");
@@ -77,8 +80,10 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
EclipseNode builderAnnotationNode = findAnnotation(Builder.class, annotatedNode);
EclipseNode superBuilderAnnotationNode = findAnnotation(SuperBuilder.class, annotatedNode);
if (builderAnnotationNode == null && superBuilderAnnotationNode == null) {
annotationNode.addWarning("@Jacksonized requires @Builder or @SuperBuilder for it to mean anything.");
EclipseNode accessorsAnnotationNode = (annotatedNode.getKind() == Kind.TYPE) ? findAnnotation(Accessors.class, annotatedNode) : null;
if (builderAnnotationNode == null && superBuilderAnnotationNode == null && accessorsAnnotationNode == null) {
annotationNode.addWarning("@Jacksonized requires @Builder, @SuperBuilder, or @Accessors for it to mean anything.");
return;
}
@@ -92,7 +97,18 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
annotationNode.addError("Builders on abstract classes cannot be @Jacksonized (the builder would never be used).");
return;
}
if (builderAnnotationNode != null || superBuilderAnnotationNode != null) {
handleJacksonizedBuilder(ast, annotationNode, annotatedNode, tdNode, td, builderAnnotationNode, superBuilderAnnotationNode);
}
if (accessorsAnnotationNode != null) {
handleJacksonizedAccessors(ast, annotationNode, annotatedNode, tdNode, td, accessorsAnnotationNode);
}
}
private void handleJacksonizedBuilder(Annotation ast, EclipseNode annotationNode, EclipseNode annotatedNode, EclipseNode tdNode, TypeDeclaration td, EclipseNode builderAnnotationNode, EclipseNode superBuilderAnnotationNode) {
AnnotationValues<Builder> builderAnnotation = builderAnnotationNode != null ? createAnnotation(Builder.class, builderAnnotationNode) : null;
AnnotationValues<SuperBuilder> superBuilderAnnotation = superBuilderAnnotationNode != null ? createAnnotation(SuperBuilder.class, superBuilderAnnotationNode) : null;
@@ -144,6 +160,38 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
builderClass.modifiers = builderClass.modifiers & ~ClassFileConstants.AccPrivate;
}
private void handleJacksonizedAccessors(Annotation ast, EclipseNode annotationNode, EclipseNode annotatedNode, EclipseNode tdNode, TypeDeclaration td, EclipseNode accessorsAnnotationNode) {
AnnotationValues<Accessors> accessorsAnnotation = accessorsAnnotationNode != null ?
createAnnotation(Accessors.class, accessorsAnnotationNode) :
null;
boolean fluent = accessorsAnnotation != null && accessorsAnnotation.getInstance().fluent();
if (!fluent) {
// No changes required for chained-only accessors.
return;
}
// Add @JsonProperty to all fields. It will be automatically copied to the getter/setters later.
for (EclipseNode eclipseNode : tdNode.down()) {
if (eclipseNode.getKind() == Kind.FIELD) {
createJsonPropertyForField(eclipseNode, annotationNode);
}
}
}
private void createJsonPropertyForField(EclipseNode fieldNode, EclipseNode annotationNode) {
if (hasAnnotation("com.fasterxml.jackson.annotation.JsonProperty", fieldNode)) {
return;
}
ASTNode astNode = fieldNode.get();
if (astNode instanceof FieldDeclaration) {
FieldDeclaration fd = (FieldDeclaration)astNode;
StringLiteral fieldName = new StringLiteral(fd.name, 0, 0, 0);
((FieldDeclaration)astNode).annotations = addAnnotation(fieldNode.get(), fd.annotations, JSON_PROPERTY_ANNOTATION, fieldName);
}
}
private String getBuilderClassName(Annotation ast, EclipseNode annotationNode, EclipseNode annotatedNode, TypeDeclaration td, AnnotationValues<Builder> builderAnnotation) {
String builderClassName = builderAnnotation != null ?
builderAnnotation.getInstance().builderClassName() : null;
@@ -228,7 +228,7 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
if (isFieldDeprecated(fieldNode) || deprecate) {
deprecated = new Annotation[] { generateDeprecatedAnnotation(source) };
}
method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated, findCopyableToSetterAnnotations(fieldNode));
method.annotations = copyAnnotations(source, onMethod.toArray(new Annotation[0]), deprecated, findCopyableToSetterAnnotations(fieldNode, false));
Argument param = new Argument(paramName, p, copyType(field.type, source), Modifier.FINAL);
param.sourceStart = pS; param.sourceEnd = pE;
method.arguments = new Argument[] { param };
@@ -1007,7 +1007,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
if (Arrays.equals(setterName.toCharArray(), existingName) && !isTolerate(fieldNode, existing[i])) return;
}
List<Annotation> methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode));
List<Annotation> methodAnnsList = Arrays.asList(EclipseHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode, true));
addCheckerFrameworkReturnsReceiver(returnType, job.source, job.checkerFramework);
MethodDeclaration setter = HandleSetter.createSetter(td, deprecate, fieldNode, setterName, paramName, nameOfSetFlag, returnType, returnStatement, ClassFileConstants.AccPublic,
job.sourceNode, methodAnnsList, annosOnParam != null ? Arrays.asList(copyAnnotations(job.source, annosOnParam)) : Collections.<Annotation>emptyList());
@@ -219,7 +219,7 @@ abstract class EclipseGuavaSingularizer extends EclipseSingularizer {
char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(builderType, new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray();
md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName(builderType, "addAll", new String(data.getPluralName())).toCharArray();
Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, data.getSource());
Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up()));
Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up(), false));
md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
@@ -195,7 +195,7 @@ abstract class EclipseJavaUtilListSetSingularizer extends EclipseJavaUtilSingula
char[] prefixedSelector = data.getSetterPrefix().length == 0 ? data.getPluralName() : HandlerUtil.buildAccessorName(builderType, new String(data.getSetterPrefix()), new String(data.getPluralName())).toCharArray();
md.selector = fluent ? prefixedSelector : HandlerUtil.buildAccessorName(builderType, "addAll", new String(data.getPluralName())).toCharArray();
Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, data.getSource());
Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up()));
Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up(), false));
md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
@@ -332,7 +332,7 @@ public class EclipseJavaUtilMapSingularizer extends EclipseJavaUtilSingularizer
md.selector = setterName.toCharArray();
Annotation[] selfReturnAnnotations = generateSelfReturnAnnotations(deprecate, data.getSource());
Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up()));
Annotation[] copyToSetterAnnotations = copyAnnotations(md, findCopyableToSetterAnnotations(data.getAnnotation().up(), false));
md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
-2
View File
@@ -27,8 +27,6 @@ import static java.lang.annotation.RetentionPolicy.*;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
@@ -116,7 +116,7 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
JavacTreeMaker maker = fieldNode.getTreeMaker();
JCExpression jsonPropertyType = chainDots(fieldNode, "com", "fasterxml", "jackson", "annotation", "JsonProperty");
JCAnnotation annotationJsonProperty = maker.Annotation(jsonPropertyType, List.of(maker.Literal(fieldNode.getName())));
JCAnnotation annotationJsonProperty = maker.Annotation(jsonPropertyType, List.<JCExpression>of(maker.Literal(fieldNode.getName())));
recursiveSetGeneratedBy(annotationJsonProperty, annotationNode);
JCVariableDecl fieldDecl = ((JCVariableDecl)fieldNode.get());
fieldDecl.mods.annotations = fieldDecl.mods.annotations.append(annotationJsonProperty);
@@ -21,9 +21,9 @@
*/
package lombok.javac.handlers;
import static lombok.javac.handlers.HandleBuilder.*;
import static lombok.core.handlers.HandlerUtil.*;
import static lombok.javac.Javac.*;
import static lombok.javac.handlers.HandleBuilder.*;
import static lombok.javac.handlers.JavacHandlerUtil.*;
import java.util.ArrayList;
@@ -968,7 +968,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode, true);
returnType = addCheckerFrameworkReturnsReceiver(returnType, maker, job.builderType, job.checkerFramework);
JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, returnType, returnStatement, job.sourceNode, methodAnns, annosOnParam, false);
JCMethodDecl newMethod = HandleSetter.createSetter(Flags.PUBLIC, deprecate, fieldNode, maker, setterName, paramName, nameOfSetFlag, returnType, returnStatement, job.sourceNode, methodAnns, annosOnParam, true);
if (job.sourceNode.up().getKind() == Kind.METHOD) {
copyJavadocFromParam(originalFieldNode.up(), newMethod, paramName.toString());
} else {
@@ -0,0 +1,16 @@
public @lombok.extern.jackson.Jacksonized @lombok.experimental.Accessors(fluent = true) @lombok.Getter @lombok.Setter class JacksonizedAccessors {
private @com.fasterxml.jackson.annotation.JsonProperty("intValue") int intValue;
public JacksonizedAccessors() {
super();
}
public @java.lang.SuppressWarnings("all") @lombok.Generated @com.fasterxml.jackson.annotation.JsonProperty("intValue") int intValue() {
return this.intValue;
}
/**
* @return {@code this}.
*/
public @java.lang.SuppressWarnings("all") @lombok.Generated @com.fasterxml.jackson.annotation.JsonProperty("intValue") JacksonizedAccessors intValue(final int intValue) {
this.intValue = intValue;
return this;
}
}
@@ -1,12 +1,7 @@
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.jackson.Jacksonized;
@Jacksonized
@Accessors(fluent = true)
@Getter
@Setter
@lombok.extern.jackson.Jacksonized
@lombok.experimental.Accessors(fluent = true)
@lombok.Getter
@lombok.Setter
public class JacksonizedAccessors {
private int intValue;
}
}