Merge branch 'JR-jacksonisedAccessors'

# Conflicts:
#	doc/changelog.markdown
This commit is contained in:
Reinier Zwitserloot committed 2025-05-23 02:57:20 +02:00
commit ef3069d0a3
36 files changed
+469 -247

No files matched your search

+2 -1
View File
@@ -2,7 +2,8 @@ Lombok Changelog
----------------
### v1.18.39 "Edgy Guinea Pig"
* No changes since v1.18.38 yet.
* FEATURE: `@Jacksonized @Accessors(fluent=true)` automatically creates the relevant annotations such that Jackson correctly identifies fluent accessors.
* IMPROBABLE BREAKING CHANGE: From versions 1.18.16 to 1.18.38, lombok automatically copies certain Jackson annotations (e.g., `@JsonProperty`) from fields to the corresponding accessors (getters/setters). However, it turned out to be harmful in certain situations. Thus, Lombok does not automatically copy those annotations any more. You can restore the old behavior using the [config key](https://projectlombok.org/features/configuration) `lombok.copyJacksonAnnotationsToAccessors = true`.
### v1.18.38 (March 31st, 2025)
* PLATFORM: JDK24 support added.
+2 -2
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2018 The Project Lombok Authors.
* Copyright (C) 2013-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -46,7 +46,7 @@ import java.lang.annotation.Target;
* <p>
* The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
* constructor / method (each field, when annotating a class), which returns the builder itself.
* The builder also has a <code>build()</code> method which returns a completed instance of the original type,
* The builder also has a {@code build()} method which returns a completed instance of the original type,
* created by passing all parameters as set via the various other methods in the builder to the constructor
* or method that was annotated with {@code @Builder}. The return type of this method will be the same
* as the relevant class, unless a method has been annotated, in which case it'll be equal to the
+13 -5
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2023 The Project Lombok Authors.
* Copyright (C) 2013-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -606,14 +606,14 @@ public class ConfigurationKeys {
/**
* lombok configuration: {@code lombok.fieldDefaults.defaultPrivate} = {@code true} | {@code false}.
*
* If set to <code>true</code> <em>any</em> field without an access modifier or {@code @PackagePrivate} is marked as {@code private} by lombok, in all source files compiled.
* If set to {@code true} <em>any</em> field without an access modifier or {@code @PackagePrivate} is marked as {@code private} by lombok, in all source files compiled.
*/
public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_PRIVATE_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultPrivate", "If true, fields without any access modifier, in any file (lombok annotated or not) are marked as private. Use @PackagePrivate or an explicit modifier to override this.") {};
/**
* lombok configuration: {@code lombok.fieldDefaults.defaultFinal} = {@code true} | {@code false}.
*
* If set to <code>true</code> <em>any</em> field without {@code @NonFinal} is marked as {@code final} by lombok, in all source files compiled.
* If set to {@code true} <em>any</em> field without {@code @NonFinal} is marked as {@code final} by lombok, in all source files compiled.
*/
public static final ConfigurationKey<Boolean> FIELD_DEFAULTS_FINAL_EVERYWHERE = new ConfigurationKey<Boolean>("lombok.fieldDefaults.defaultFinal", "If true, fields, in any file (lombok annotated or not) are marked as final. Use @NonFinal to override this.") {};
@@ -721,7 +721,7 @@ public class ConfigurationKeys {
* If set to {@code true}, no further {@code lombok.config} files will be checked.
*/
public static final ConfigurationKey<Boolean> STOP_BUBBLING = new ConfigurationKey<Boolean>("config.stopBubbling", "Tell the configuration system it should stop looking for other configuration files (default: false).") {};
/**
* lombok configuration: {@code lombok.copyableAnnotations} += &lt;TypeName: fully-qualified annotation class name&gt;.
*
@@ -737,11 +737,19 @@ public class ConfigurationKeys {
* checkerframework.org version will be generated.
*/
public static final ConfigurationKey<CheckerFrameworkVersion> CHECKER_FRAMEWORK = new ConfigurationKey<CheckerFrameworkVersion>("checkerframework", "If set with the version of checkerframework.org (in major.minor, or just 'true' for the latest supported version), create relevant checkerframework.org annotations for code lombok generates (default: false).") {};
/**
* lombok configuration: {@code lombok.standardException.flagUsage} = {@code WARNING} | {@code ERROR}.
*
* If set, <em>any</em> usage of {@code @StandardException} results in a warning / error.
*/
public static final ConfigurationKey<FlagUsageType> STANDARD_EXCEPTION_FLAG_USAGE = new ConfigurationKey<FlagUsageType>("lombok.standardException.flagUsage", "Emit a warning or error if @StandardException is used.") {};
/**
* lombok configuration: {@code lombok.copyJacksonAnnotationsToAccessors} = {@code true} | {@code false}.
*
* If {@code true}, copy certain Jackson annotations from a field to its corresponding accessors (getter/setters). This was the behavior from lombok 1.18.16 to 1.18.38.
* However, it turned out to be harmful in certain situations. Thus, the default is now {@code false}.
*/
public static final ConfigurationKey<Boolean> COPY_JACKSON_ANNOTATIONS_TO_ACCESSORS = new ConfigurationKey<Boolean>("lombok.copyJacksonAnnotationsToAccessors", "Copy Jackson annotations from fields to the corresponding getters and setters.") {};
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 The Project Lombok Authors.
* Copyright (C) 2019-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,9 @@ package lombok.core.configuration;
* If a type used in {@link ConfigurationKey} type argument implements this interface,
* it is expected to provide the following three static methods:
* <ul>
* <li><code>public static SELF valueOf(String value)</code>
* <li><code>public static String description()</code>
* <li><code>public static String exampleValue()</code>
* <li><code>public static SELF valueOf(String value)</code></li>
* <li><code>public static String description()</code></li>
* <li><code>public static String exampleValue()</code></li>
* </ul>
* None of them should throw checked exceptions.
* Based on these methods, an instance of {@link ConfigurationValueParser} is created
+35 -3
View File
@@ -79,7 +79,7 @@ public class HandlerUtil {
return 43;
}
public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, COPY_TO_SETTER_ANNOTATIONS, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_ANNOTATIONS;
public static final List<String> NONNULL_ANNOTATIONS, BASE_COPYABLE_ANNOTATIONS, JACKSON_COPY_TO_GETTER_ANNOTATIONS, JACKSON_COPY_TO_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS, JACKSON_COPY_TO_BUILDER_ANNOTATIONS;
static {
// This is a list of annotations with a __highly specific meaning__: All annotations in this list indicate that passing null for the relevant item is __never__ acceptable, regardless of settings or circumstance.
// In other words, things like 'this models a database table, and the db table column has a nonnull constraint', or 'this represents a web form, and if this is null, the form is invalid' __do not count__ and should not be in this list;
@@ -424,7 +424,37 @@ public class HandlerUtil {
"org.checkerframework.common.value.qual.UnknownVal",
"org.checkerframework.framework.qual.PurityUnqualified",
}));
COPY_TO_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
// The following two lists contain all annotations that can be copied from the field to the getter or setter.
// Right now, it only contains Jackson annotations.
// Jackson's annotation processing roughly works as follows: To calculate the annotation for a JSON property, Jackson
// builds a triple of the Java field and the corresponding setter and getter methods. It is sufficient for an annotation
// to be present on one of those to become effective. E.g., a @JsonIgnore on a setter completely ignores the JSON property,
// not only during deserialization, but also when serializing. Therefore, in most cases it is _not_ necessary to copy the
// annotations. It may even harm, as Jackson considers some annotations inheritable, and this "virtual inheritance" only
// affects annotations on setter/getter, but not on private fields.
// However, there are two exceptions where we have to copy the annotations:
// 1. When using a builder to deserialize, Jackson does _not_ "propagate" the annotations to the setter methods of the
// builder, i.e. annotations like @JsonIgnore on the field will not be respected when deserializing with a builder.
// Thus, those annotations should be copied to the builder's setters.
// 2. If the getter/setter methods do not follow the exact beanspec naming strategy, Jackson will not correctly detect the
// field-getter-setter triple, and annotations may not work as intended.
// However, we cannot always know what the user's intention is. Thus, lombok should only fix those cases where it is
// obvious what the user wants. That is the case for a `@Jacksonized @Accessors(fluent=true)`.
JACKSON_COPY_TO_GETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
"com.fasterxml.jackson.annotation.JsonFormat",
"com.fasterxml.jackson.annotation.JsonIgnore",
"com.fasterxml.jackson.annotation.JsonIgnoreProperties",
"com.fasterxml.jackson.annotation.JsonProperty",
"com.fasterxml.jackson.annotation.JsonSubTypes",
"com.fasterxml.jackson.annotation.JsonTypeInfo",
"com.fasterxml.jackson.annotation.JsonUnwrapped",
"com.fasterxml.jackson.annotation.JsonView",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText",
}));
JACKSON_COPY_TO_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
"com.fasterxml.jackson.annotation.JacksonInject",
"com.fasterxml.jackson.annotation.JsonAlias",
"com.fasterxml.jackson.annotation.JsonFormat",
@@ -441,9 +471,11 @@ public class HandlerUtil {
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty",
"com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlText",
}));
COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
JACKSON_COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
"com.fasterxml.jackson.annotation.JsonAnySetter",
}));
// In order to let Jackson recognize certain configuration annotations when deserializing using a builder, those must
// be copied to the generated builder class.
JACKSON_COPY_TO_BUILDER_ANNOTATIONS = Collections.unmodifiableList(Arrays.asList(new String[] {
"com.fasterxml.jackson.annotation.JsonAutoDetect",
"com.fasterxml.jackson.annotation.JsonFormat",
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2023 The Project Lombok Authors.
* Copyright (C) 2009-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -129,14 +129,14 @@ public class TransformEclipseAST {
/**
* Check if lombok already handled the given AST. This method will return
* <code>true</code> once for diet mode and once for full mode.
* {@code true} once for diet mode and once for full mode.
*
* The reason for this is that Eclipse invokes the transform method multiple
* times during compilation and it is enough to transform it once and not
* repeat the whole thing over and over again.
*
* @param ast The AST node belonging to the compilation unit (java speak for a single source file).
* @return <code>true</code> if this AST was already handled by lombok.
* @return {@code true} if this AST was already handled by lombok.
*/
public static boolean alreadyTransformed(CompilationUnitDeclaration ast) {
TransformationState state = CompilationUnitDeclaration_transformationState.get(ast);
@@ -820,17 +820,36 @@ public class EclipseHandlerUtil {
}
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the setter.
* Searches the given field node for annotations that are specifically intended to be copied to the getter.
*
* @param forceCopyJacksonAnnotations If {@code true}, always copy the annotations regardless of regardless of lombok configuration key {@code lombok.copyJacksonAnnotationsToAccessors}.
*/
public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node) {
return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS);
public static Annotation[] findCopyableToGetterAnnotations(EclipseNode node, boolean forceCopyJacksonAnnotations) {
if (!forceCopyJacksonAnnotations) {
Boolean copyAnnotations = node.getAst().readConfiguration(ConfigurationKeys.COPY_JACKSON_ANNOTATIONS_TO_ACCESSORS);
if (copyAnnotations == null || !copyAnnotations) return EMPTY_ANNOTATIONS_ARRAY;
}
return findAnnotationsInList(node, JACKSON_COPY_TO_GETTER_ANNOTATIONS);
}
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the builder's singular method.
* Searches the given field node for annotations that are specifically intended to be copied to the setter.
*
* @param forceCopyJacksonAnnotations If {@code true}, always copy the annotations regardless of regardless of lombok configuration key {@code lombok.copyJacksonAnnotationsToAccessors}.
*/
public static Annotation[] findCopyableToSetterAnnotations(EclipseNode node, boolean forceCopyJacksonAnnotations) {
if (!forceCopyJacksonAnnotations) {
Boolean copyAnnotations = node.getAst().readConfiguration(ConfigurationKeys.COPY_JACKSON_ANNOTATIONS_TO_ACCESSORS);
if (copyAnnotations == null || !copyAnnotations) return EMPTY_ANNOTATIONS_ARRAY;
}
return findAnnotationsInList(node, JACKSON_COPY_TO_SETTER_ANNOTATIONS);
}
/**
* Searches the given field node for annotations that are specifically intended to be copied to the builder's singular method.
*/
public static Annotation[] findCopyableToBuilderSingularSetterAnnotations(EclipseNode node) {
return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS);
return findAnnotationsInList(node, JACKSON_COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021 The Project Lombok Authors.
* Copyright (C) 2015-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -422,7 +422,7 @@ public class EclipseSingularsRecipes {
return new QualifiedTypeReference(TypeConstants.JAVA_LANG_OBJECT, NULL_POSS);
}
/** @return a {@code SingleNameReference} to the builder in the variable <code>builderVariable</code>. If {@ code builderVariable == "this"}, a {@code ThisReference} is returned. */
/** @return a {@code SingleNameReference} to the builder in the variable {@code builderVariable}. If {@code builderVariable == "this"}, a {@code ThisReference} is returned. */
protected static Reference getBuilderReference(String builderVariable) {
if ("this".equals(builderVariable)) {
return new ThisReference(0, 0);
@@ -1042,7 +1042,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),
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2024 The Project Lombok Authors.
* Copyright (C) 2009-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -83,7 +83,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
public class HandleGetter extends EclipseAnnotationHandler<Getter> {
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
private static final String GETTER_NODE_NOT_SUPPORTED_ERR = "@Getter is only supported on a class, an enum, or a field.";
public boolean generateGetterForType(EclipseNode typeNode, EclipseNode pos, AccessLevel level, boolean checkForTypeLevelGetter, List<Annotation> onMethod) {
if (checkForTypeLevelGetter) {
if (hasAnnotation(Getter.class, typeNode)) {
@@ -279,9 +279,15 @@ public class HandleGetter extends EclipseAnnotationHandler<Getter> {
if (getCheckerFrameworkVersion(fieldNode).generateSideEffectFree()) checkerFramework = new Annotation[] { generateNamedAnnotation(source, CheckerFrameworkVersion.NAME__SIDE_EFFECT_FREE) };
}
// Copying Jackson annotations is required for fluent accessors (otherwise Jackson would not find the accessor).
boolean fluent = accessors.isExplicit("fluent");
Boolean fluentConfig = fieldNode.getAst().readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT);
if (fluentConfig != null && fluentConfig) fluent = fluentConfig;
method.annotations = copyAnnotations(source,
onMethod.toArray(new Annotation[0]),
findCopyableAnnotations(fieldNode),
findCopyableToGetterAnnotations(fieldNode, fluent),
findDelegatesAndMarkAsHandled(fieldNode),
checkerFramework,
deprecated);
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2021 The Project Lombok Authors.
* Copyright (C) 2020-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -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,23 +64,24 @@ 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");
EclipseNode annotatedNode = annotationNode.up();
EclipseNode tdNode;
if (annotatedNode.getKind() != Kind.TYPE)
tdNode = annotatedNode.up(); // @Jacksonized on a constructor or a static factory method.
else
tdNode = annotatedNode; // @Jacksonized on the class.
if (annotatedNode.getKind() != Kind.TYPE) tdNode = annotatedNode.up(); // @Jacksonized on a constructor or a static factory method.
else tdNode = annotatedNode; // @Jacksonized on the class.
TypeDeclaration td = (TypeDeclaration) tdNode.get();
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;
}
@@ -87,6 +90,17 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
return;
}
boolean jacksonizedBuilder = builderAnnotationNode != null || superBuilderAnnotationNode != null;
if (jacksonizedBuilder) {
handleJacksonizedBuilder(ast, annotationNode, annotatedNode, tdNode, td, builderAnnotationNode, superBuilderAnnotationNode);
}
if (accessorsAnnotationNode != null) {
handleJacksonizedAccessors(ast, annotationNode, annotatedNode, tdNode, td, accessorsAnnotationNode, jacksonizedBuilder);
}
}
private void handleJacksonizedBuilder(Annotation ast, EclipseNode annotationNode, EclipseNode annotatedNode, EclipseNode tdNode, TypeDeclaration td, EclipseNode builderAnnotationNode, EclipseNode superBuilderAnnotationNode) {
boolean isAbstract = (td.modifiers & ClassFileConstants.AccAbstract) != 0;
if (isAbstract) {
annotationNode.addError("Builders on abstract classes cannot be @Jacksonized (the builder would never be used).");
@@ -144,14 +158,45 @@ 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, boolean jacksonizedBuilder) {
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.
if (!jacksonizedBuilder) {
annotationNode.addWarning("@Jacksonized only affects fluent accessors (@Accessors(fluent=true)).");
}
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);
}
}
tdNode.rebuild();
}
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;
if (builderClassName == null || builderClassName.isEmpty()) {
builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME);
if (builderClassName == null || builderClassName.isEmpty())
builderClassName = "*Builder";
if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder";
MethodDeclaration fillParametersFrom = annotatedNode.get() instanceof MethodDeclaration ? (MethodDeclaration) annotatedNode.get() : null;
char[] replacement;
if (fillParametersFrom != null) {
@@ -163,23 +208,22 @@ public class HandleJacksonized extends EclipseAnnotationHandler<Jacksonized> {
}
builderClassName = builderClassName.replace("*", new String(replacement));
}
if (builderAnnotation == null)
builderClassName += "Impl"; // For @SuperBuilder, all Jackson annotations must be put on the BuilderImpl class.
if (builderAnnotation == null) builderClassName += "Impl"; // For @SuperBuilder, all Jackson annotations must be put on the BuilderImpl class.
return builderClassName;
}
private static final Annotation[] EMPTY_ANNOTATIONS_ARRAY = new Annotation[0];
private static Annotation[] findJacksonAnnotationsOnClass(TypeDeclaration td, EclipseNode node) {
if (td.annotations == null) return EMPTY_ANNOTATIONS_ARRAY;
List<Annotation> result = new ArrayList<Annotation>();
for (Annotation annotation : td.annotations) {
TypeReference typeRef = annotation.type;
if (typeRef != null && typeRef.getTypeName() != null) {
for (String bn : HandlerUtil.JACKSON_COPY_TO_BUILDER_ANNOTATIONS) {
for (String bn : HandlerUtil.JACKSON_COPY_TO_BUILDER_ANNOTATIONS) {
if (typeMatches(bn, node, typeRef)) {
result.add(annotation);
break;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2024 The Project Lombok Authors.
* Copyright (C) 2009-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -27,6 +27,7 @@ import static lombok.eclipse.handlers.EclipseHandlerUtil.*;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@@ -109,6 +110,14 @@ public class HandleSetter extends EclipseAnnotationHandler<Setter> {
//The annotation will make it happen, so we can skip it.
return;
}
// If a fluent setter should be generated, force-copy annotations from the field.
AnnotationValues<Accessors> accessorsForField = EclipseHandlerUtil.getAccessorsForField(fieldNode);
// Copying Jackson annotations is required for fluent accessors (otherwise Jackson would not find the accessor).
Annotation[] copyableToSetterAnnotations = findCopyableToSetterAnnotations(fieldNode, accessorsForField.isExplicit("fluent"));
onMethod = new ArrayList<Annotation>(onMethod);
onMethod.addAll(Arrays.asList(copyableToSetterAnnotations));
createSetterForField(level, fieldNode, sourceNode, false, onMethod, onParam);
}
@@ -228,7 +237,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 };
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2021 The Project Lombok Authors.
* Copyright (C) 2013-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -162,13 +162,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
job.init(annotation, annInstance, annotationNode);
boolean generateBuilderMethod;
if (job.builderMethodName.isEmpty()) {
generateBuilderMethod = false;
} else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) {
return;
} else {
generateBuilderMethod = true;
}
if (job.builderMethodName.isEmpty()) generateBuilderMethod = false;
else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) return;
else generateBuilderMethod = true;
if (!checkName("buildMethodName", job.buildMethodName, annotationNode)) return;
@@ -343,9 +339,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
if (sd == null) continue;
EclipseSingularizer singularizer = sd.getSingularizer();
if (singularizer == null) continue;
if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) {
bfd.singularData = null;
}
if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) bfd.singularData = null;
}
}
@@ -407,9 +401,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
}
// Let toString() call super.toString() if there is a superclass, so that it also shows fields from the superclass' builder.
MethodDeclaration md = HandleToString.createToString(job.builderType, fieldNodes, true, superclassBuilderClass != null, ast, FieldAccess.ALWAYS_FIELD);
if (md != null) {
injectMethod(job.builderType, md);
}
if (md != null) injectMethod(job.builderType, md);
}
if (addCleaning) {
@@ -593,7 +585,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
char[][] setVariableInBuilder = new char[][] {BUILDER_VARIABLE_NAME, fieldNode.nameOfSetFlag};
long[] positions = new long[] {p, p};
QualifiedNameReference setVariableInBuilderRef = new QualifiedNameReference(setVariableInBuilder, positions, s, e);
MessageSend defaultMethodCall = new MessageSend();
defaultMethodCall.sourceStart = job.source.sourceStart;
defaultMethodCall.sourceEnd = job.source.sourceEnd;
@@ -657,7 +649,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
}
/**
* Generates a <code>toBuilder()</code> method in the annotated class that looks like this:
* Generates a {@code toBuilder()} method in the annotated class that looks like this:
* <pre>
* public <i>Foobar</i>.<i>Foobar</i>Builder&lt;?, ?&gt; toBuilder() {
* return new <i.Foobar</i>.<i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
@@ -696,10 +688,10 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
out.traverse(new SetGeneratedByVisitor(job.source), ((TypeDeclaration) job.parentType.get()).scope);
return out;
}
/**
* Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
* like this:
* Generates a {@code $fillValuesFrom()} method in the abstract builder class.
* It looks like:
* <pre>
* protected B $fillValuesFrom(final C instance) {
* super.$fillValuesFrom(instance);
@@ -718,9 +710,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
TypeReference builderType = new SingleTypeReference(classGenericName.toCharArray(), 0);
out.arguments = new Argument[] {new Argument(INSTANCE_VARIABLE_NAME, 0, builderType, Modifier.FINAL)};
List<Statement> body = new ArrayList<Statement>();
if (inherited) {
// Call super.
MessageSend callToSuper = new MessageSend();
@@ -729,14 +721,14 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
callToSuper.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0)};
body.add(callToSuper);
}
// Call the builder implemention's helper method that actually fills the values from the instance.
MessageSend callStaticFillValuesMethod = new MessageSend();
callStaticFillValuesMethod.receiver = generateNameReference(job.parentType, job.builderAbstractClassNameArr, 0);
callStaticFillValuesMethod.selector = FILL_VALUES_STATIC_METHOD_NAME;
callStaticFillValuesMethod.arguments = new Expression[] {new SingleNameReference(INSTANCE_VARIABLE_NAME, 0), new ThisReference(0, 0)};
body.add(callStaticFillValuesMethod);
// Return self().
MessageSend returnCall = new MessageSend();
returnCall.receiver = ThisReference.implicitThis();
@@ -747,9 +739,9 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
return out;
}
/**
* Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
* Generates a {@code $fillValuesFromInstanceIntoBuilder()} method in
* the builder implementation class that copies all fields from the instance
* to the builder. It looks like this:
*
@@ -1007,7 +999,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());
@@ -1095,8 +1087,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
java.util.HashSet<String> usedNames = new HashSet<String>();
// 1. Add type parameter names.
for (TypeParameter typeParam : typeParams)
usedNames.add(typeParam.toString());
for (TypeParameter typeParam : typeParams) usedNames.add(typeParam.toString());
// 2. Add class name.
usedNames.add(String.valueOf(td.name));
@@ -1119,14 +1110,12 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
return usedNames;
}
private void addFirstToken(java.util.Set<String> usedNames, TypeReference type) {
if (type == null)
return;
if (type == null) return;
// Add the first token, because only that can collide.
char[][] typeName = type.getTypeName();
if (typeName != null && typeName.length >= 1)
usedNames.add(String.valueOf(typeName[0]));
if (typeName != null && typeName.length >= 1) usedNames.add(String.valueOf(typeName[0]));
}
private String generateNonclashingNameFor(String classGenericName, java.util.Set<String> typeParamStrings) {
@@ -1227,8 +1216,7 @@ public class HandleSuperBuilder extends EclipseAnnotationHandler<SuperBuilder> {
// Cannot use typeMatches() here, because the parameter could be fully-qualified, partially-qualified, or not qualified.
// A string-compare of the last part should work. If it's a false-positive, users could still @Tolerate it.
char[] typeName = def.arguments[0].type.getLastToken();
if (builderClassName.equals(String.valueOf(typeName)))
return true;
if (builderClassName.equals(String.valueOf(typeName))) return true;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021 The Project Lombok Authors.
* Copyright (C) 2015-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -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(), true));
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(), true));
md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021 The Project Lombok Authors.
* Copyright (C) 2015-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -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(), true));
md.annotations = concat(selfReturnAnnotations, copyToSetterAnnotations, Annotation.class);
if (returnStatement != null) createRelevantNonNullAnnotation(builderType, md);
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021 The Project Lombok Authors.
* Copyright (C) 2015-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 The Project Lombok Authors.
* Copyright (C) 2018-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -40,7 +40,7 @@ import lombok.Singular;
* <p>
* The <code><strong>T</strong>Builder</code> class contains 1 method for each parameter of the annotated
* constructor / method (each field, when annotating a class), which returns the builder itself.
* The builder also has a <code>build()</code> method which returns a completed instance of the original type.
* The builder also has a {@code build()} method which returns a completed instance of the original type.
* <p>
* Complete documentation is found at <a href="https://projectlombok.org/features/experimental/SuperBuilder">the project lombok features page for &#64;SuperBuilder</a>.
*
@@ -56,8 +56,8 @@ public @interface SuperBuilder {
String buildMethodName() default "build";
/**
* If <code>true</code>, generate an instance method to obtain a builder that is initialized with the values of this instance.
* In this case, all superclasses must also have <code>toBuilder=true</code>.
* If {@code true}, generate an instance method to obtain a builder that is initialized with the values of this instance.
* In this case, all superclasses must also have {@code toBuilder=true}.
*
* @return Whether to generate a {@code toBuilder()} method.
*/
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2019 The Project Lombok Authors.
* Copyright (C) 2015-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -34,7 +34,7 @@ import java.lang.annotation.Target;
* <li>If any constructors are declared in it, an error is generated. Otherwise, a private no-args constructor is generated; it throws a {@code UnsupportedOperationException}.</li>
* <li>All methods, inner classes, and fields in the class are marked static.</li>
* <li><em>WARNING:</em> Do not use non-star static imports to import these members; javac won't be able to figure it out. Use either:
* <code>import static ThisType.*;</code> or don't static-import.</li>
* {@code import static ThisType.*;} or don't static-import.</li>
* </ul>
*/
@Target({ElementType.TYPE})
+17 -8
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020 The Project Lombok Authors.
* Copyright (C) 2020-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -28,12 +28,21 @@ import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import lombok.Builder;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
/**
* The {@code @Jacksonized} annotation is an add-on annotation for
* {@code @}{@link Builder} and {@code @}{@link SuperBuilder}. It automatically
* configures the generated builder class to be used by Jackson's
* {@code @}{@link Builder}, {@code @}{@link SuperBuilder}, and
* {@code @}{@link Accessors}.
* <p>
* For {@code @}{@link Accessors}{@code (fluent = true)} on a type, it automatically
* configures Jackson to use the generated setters and getters for
* (de-)serialization by inserting {@code @}{@link JsonProperty} annotations.
* (Note that {@code @Jacksonized} {@code @Accessors} on fields are not supported.)
* <p>
* For {@code @}{@link Builder} and {@code @}{@link SuperBuilder}, it
* automatically configures the generated builder class to be used by Jackson's
* deserialization. It only has an effect if present at a context where there is
* also a {@code @Builder} or a {@code @SuperBuilder}; a warning is emitted
* otherwise.
@@ -41,8 +50,8 @@ import lombok.experimental.SuperBuilder;
* In particular, the annotation does the following:
* <ul>
* <li>Configure Jackson to use the builder for deserialization using
* {@code @JsonDeserialize(builder=Foobar.FoobarBuilder[Impl].class)}
* on the class (where <em>Foobar</em> is the name of the annotated class).</li>
* {@code @JsonDeserialize(builder=Foobar.FoobarBuilder[Impl].class)} on the
* class (where <em>Foobar</em> is the name of the annotated class).</li>
* <li>Copy Jackson-related configuration annotations (like
* {@code @JsonIgnoreProperties}) from the class to the builder class. This is
* necessary so that Jackson recognizes them when using the builder.</li>
@@ -54,9 +63,9 @@ import lombok.experimental.SuperBuilder;
* <li>For {@code @SuperBuilder}, make the builder implementation class
* package-private.</li>
* </ul>
* This annotation does <em>not</em> change the behavior of the generated builder.
* A {@code @Jacksonized} {@code @SuperBuilder} remains fully compatible to
* regular {@code @SuperBuilder}s.
* This annotation does <em>not</em> change the behavior of the generated
* builder. A {@code @Jacksonized} {@code @SuperBuilder} remains fully
* compatible to regular {@code @SuperBuilder}s.
*/
@Target({TYPE, METHOD, CONSTRUCTOR})
@Retention(SOURCE)
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2021 The Project Lombok Authors.
* Copyright (C) 2013-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -930,7 +930,7 @@ public class HandleBuilder extends JavacAnnotationHandler<Builder> {
JavacTreeMaker maker = fieldNode.getTreeMaker();
List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(bfd.originalFieldNode);
List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(bfd.originalFieldNode, true);
JCMethodDecl newMethod = HandleSetter.createSetter(toJavacModifier(job.accessInners), deprecate, fieldNode, maker, setterName, bfd.name, bfd.nameOfSetFlag, job.oldChain, job.sourceNode, methodAnns, bfd.annotations);
recursiveSetGeneratedBy(newMethod, job.sourceNode);
if (job.sourceNode.up().getKind() == Kind.METHOD) {
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2024 The Project Lombok Authors.
* Copyright (C) 2009-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -249,8 +249,15 @@ public class HandleGetter extends JavacAnnotationHandler<Getter> {
JCExpression annotationMethodDefaultValue = null;
List<JCAnnotation> copyableAnnotations = findCopyableAnnotations(field);
// Copying Jackson annotations is required for fluent accessors (otherwise Jackson would not find the accessor).
boolean fluent = accessors.isExplicit("fluent");
Boolean fluentConfig = field.getAst().readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT);
if (fluentConfig != null && fluentConfig) fluent = fluentConfig;
List<JCAnnotation> copyableToGetterAnnotations = copyAnnotations(findCopyableToGetterAnnotations(field, fluent), treeMaker);
List<JCAnnotation> delegates = findDelegatesAndRemoveFromField(field);
List<JCAnnotation> annsOnMethod = copyAnnotations(onMethod, treeMaker).appendList(copyableAnnotations);
List<JCAnnotation> annsOnMethod = copyAnnotations(onMethod, treeMaker).appendList(copyableAnnotations).appendList(copyableToGetterAnnotations);
if (field.isFinal()) {
if (getCheckerFrameworkVersion(field).generatePure()) annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genTypeRef(field, CheckerFrameworkVersion.NAME__PURE), List.<JCExpression>nil()));
} else {
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2021 The Project Lombok Authors.
* Copyright (C) 2020-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,6 +33,7 @@ import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.ListBuffer;
@@ -42,6 +43,7 @@ import lombok.core.AST.Kind;
import lombok.core.AnnotationValues;
import lombok.core.HandlerPriority;
import lombok.core.handlers.HandlerUtil;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;
import lombok.extern.jackson.Jacksonized;
import lombok.javac.JavacAnnotationHandler;
@@ -55,34 +57,76 @@ import lombok.spi.Provides;
* needs for builders.
*/
@Provides
@HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated).
@HandlerPriority(-512) // Above Handle(Super)Builder's level (builders must be already generated), but before all handlers generating getters/setters.
public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
@Override public void handle(AnnotationValues<Jacksonized> annotation, JCAnnotation ast, JavacNode annotationNode) {
handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.JACKSONIZED_FLAG_USAGE, "@Jacksonized");
JavacNode annotatedNode = annotationNode.up();
deleteAnnotationIfNeccessary(annotationNode, Jacksonized.class);
JavacNode tdNode;
if (annotatedNode.getKind() != Kind.TYPE)
tdNode = annotatedNode.up(); // @Jacksonized on a constructor or a static factory method.
else
tdNode = annotatedNode; // @Jacksonized on the class.
if (annotatedNode.getKind() != Kind.TYPE) tdNode = annotatedNode.up(); // @Jacksonized on a constructor or a static factory method.
else tdNode = annotatedNode; // @Jacksonized on the class.
JCClassDecl td = (JCClassDecl) tdNode.get();
JavacNode builderAnnotationNode = findAnnotation(Builder.class, annotatedNode);
JavacNode superBuilderAnnotationNode = findAnnotation(SuperBuilder.class, annotatedNode);
if (builderAnnotationNode == null && superBuilderAnnotationNode == null) {
annotationNode.addWarning("@Jacksonized requires @Builder or @SuperBuilder for it to mean anything.");
JavacNode accessorsAnnotationNode = findAnnotation(Accessors.class, annotatedNode);
if (builderAnnotationNode == null && superBuilderAnnotationNode == null && accessorsAnnotationNode == null) {
annotationNode.addWarning("@Jacksonized requires @Builder, @SuperBuilder, or @Accessors for it to mean anything.");
return;
}
if (builderAnnotationNode != null || superBuilderAnnotationNode != null) {
handleJacksonizedBuilder(annotationNode, annotatedNode, tdNode, td, builderAnnotationNode, superBuilderAnnotationNode);
}
if (accessorsAnnotationNode != null) {
handleJacksonizedAccessors(annotationNode, annotatedNode, tdNode, td, accessorsAnnotationNode, builderAnnotationNode != null || superBuilderAnnotationNode != null);
}
}
private void handleJacksonizedAccessors(JavacNode annotationNode, JavacNode annotatedNode, JavacNode tdNode, JCClassDecl td, JavacNode accessorsAnnotationNode, boolean jacksonizedBuilder) {
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.
if (!jacksonizedBuilder)
annotationNode.addWarning("@Jacksonized only affects fluent accessors (@Accessors(fluent=true)).");
return;
}
// Add @JsonProperty to all fields. It will be automatically copied to the getter/setters later.
for (JavacNode javacNode : tdNode.down()) {
if (javacNode.getKind() == Kind.FIELD) {
createJsonPropertyForField(javacNode, annotationNode);
}
}
}
private void createJsonPropertyForField(JavacNode fieldNode, JavacNode annotationNode) {
if (hasAnnotation("com.fasterxml.jackson.annotation.JsonProperty", fieldNode)) {
return;
}
JavacTreeMaker maker = fieldNode.getTreeMaker();
JCExpression jsonPropertyType = chainDots(fieldNode, "com", "fasterxml", "jackson", "annotation", "JsonProperty");
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);
}
private void handleJacksonizedBuilder(JavacNode annotationNode, JavacNode annotatedNode, JavacNode tdNode, JCClassDecl td, JavacNode builderAnnotationNode, JavacNode superBuilderAnnotationNode) {
if (builderAnnotationNode != null && superBuilderAnnotationNode != null) {
annotationNode.addError("@Jacksonized cannot process both @Builder and @SuperBuilder on the same class.");
return;
}
boolean isAbstract = (td.mods.flags & Flags.ABSTRACT) != 0;
if (isAbstract) {
annotationNode.addError("Builders on abstract classes cannot be @Jacksonized (the builder would never be used).");
@@ -90,24 +134,22 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
}
AnnotationValues<Builder> builderAnnotation = builderAnnotationNode != null ?
createAnnotation(Builder.class, builderAnnotationNode) :
null;
createAnnotation(Builder.class, builderAnnotationNode) : null;
AnnotationValues<SuperBuilder> superBuilderAnnotation = superBuilderAnnotationNode != null ?
createAnnotation(SuperBuilder.class, superBuilderAnnotationNode) :
null;
createAnnotation(SuperBuilder.class, superBuilderAnnotationNode) : null;
String setPrefix = builderAnnotation != null ?
builderAnnotation.getInstance().setterPrefix() :
superBuilderAnnotation.getInstance().setterPrefix();
superBuilderAnnotation.getInstance().setterPrefix();
String buildMethodName = builderAnnotation != null ?
builderAnnotation.getInstance().buildMethodName() :
superBuilderAnnotation.getInstance().buildMethodName();
superBuilderAnnotation.getInstance().buildMethodName();
JavacTreeMaker maker = annotatedNode.getTreeMaker();
// Now lets find the generated builder class.
String builderClassName = getBuilderClassName(annotationNode, annotatedNode, td, builderAnnotation, maker);
JCClassDecl builderClass = null;
for (JCTree member : td.getMembers()) {
if (member instanceof JCClassDecl && ((JCClassDecl) member).getSimpleName().contentEquals(builderClassName)) {
@@ -149,20 +191,17 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
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;
}
if (superBuilderAnnotationNode != null) builderClass.mods.flags = builderClass.mods.flags & ~Flags.PRIVATE;
}
private String getBuilderClassName(JavacNode annotationNode, JavacNode annotatedNode, JCClassDecl td, AnnotationValues<Builder> builderAnnotation, JavacTreeMaker maker) {
String builderClassName = builderAnnotation != null ?
builderAnnotation.getInstance().builderClassName() : null;
String builderClassName = builderAnnotation != null ? builderAnnotation.getInstance().builderClassName() : null;
if (builderClassName == null || builderClassName.isEmpty()) {
builderClassName = annotationNode.getAst().readConfiguration(ConfigurationKeys.BUILDER_CLASS_NAME);
if (builderClassName == null || builderClassName.isEmpty())
builderClassName = "*Builder";
if (builderClassName == null || builderClassName.isEmpty()) builderClassName = "*Builder";
JCMethodDecl fillParametersFrom = annotatedNode.get() instanceof JCMethodDecl ? (JCMethodDecl)annotatedNode.get() : null;
String replacement;
if (fillParametersFrom != null && !fillParametersFrom.getName().toString().equals("<init>")) {
@@ -179,9 +218,8 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
}
builderClassName = builderClassName.replace("*", replacement);
}
if (builderAnnotation == null)
builderClassName += "Impl"; // For @SuperBuilder, all Jackson annotations must be put on the BuilderImpl class.
if (builderAnnotation == null) builderClassName += "Impl"; // For @SuperBuilder, all Jackson annotations must be put on the BuilderImpl class.
return builderClassName;
}
@@ -191,7 +229,7 @@ public class HandleJacksonized extends JavacAnnotationHandler<Jacksonized> {
for (JavacNode child : node.down()) {
if (child.getKind() == Kind.ANNOTATION) {
JCAnnotation annotation = (JCAnnotation) child.get();
for (String bn : HandlerUtil.JACKSON_COPY_TO_BUILDER_ANNOTATIONS) {
for (String bn : HandlerUtil.JACKSON_COPY_TO_BUILDER_ANNOTATIONS) {
if (typeMatches(bn, node, annotation.annotationType)) {
result.append(annotation);
break;
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2009-2024 The Project Lombok Authors.
* Copyright (C) 2009-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -211,7 +211,7 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
return createSetter(access, deprecate, field, treeMaker, setterName, paramName, booleanFieldToSet, returnType, returnStatement, source, onMethod, onParam);
}
public static JCMethodDecl createSetterWithRecv(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam, JCVariableDecl recv) {
public static JCMethodDecl createSetterWithRecv(long access, boolean deprecate, JavacNode field, JavacTreeMaker treeMaker, String setterName, Name paramName, Name booleanFieldToSet, boolean shouldReturnThis, JavacNode source, List<JCAnnotation> onMethod, List<JCAnnotation> onParam, JCVariableDecl recv, boolean forceAnnotationCopying) {
JCExpression returnType = null;
JCReturn returnStatement = null;
if (shouldReturnThis) {
@@ -274,12 +274,17 @@ public class HandleSetter extends JavacAnnotationHandler<Setter> {
List<JCExpression> throwsClauses = List.nil();
JCExpression annotationMethodDefaultValue = null;
List<JCAnnotation> annsOnMethod = mergeAnnotations(copyAnnotations(onMethod, treeMaker), findCopyableToSetterAnnotations(field));
// Copying Jackson annotations is required for fluent accessors (otherwise Jackson would not find the accessor).
AnnotationValues<Accessors> accessors = JavacHandlerUtil.getAccessorsForField(field);
boolean fluent = accessors.isExplicit("fluent");
Boolean fluentConfig = field.getAst().readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT);
if (fluentConfig != null && fluentConfig) fluent = fluentConfig;
List<JCAnnotation> annsOnMethod = mergeAnnotations(copyAnnotations(onMethod, treeMaker), findCopyableToSetterAnnotations(field, fluent));
if (isFieldDeprecated(field) || deprecate) {
annsOnMethod = annsOnMethod.prepend(treeMaker.Annotation(genJavaLangTypeRef(field, "Deprecated"), List.<JCExpression>nil()));
}
AnnotationValues<Accessors> accessors = JavacHandlerUtil.getAccessorsForField(field);
if (shouldMakeFinal(field, accessors)) access |= Flags.FINAL;
JCMethodDecl methodDef;
if (recv != null && treeMaker.hasMethodDefWithRecvParam()) {
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2021 The Project Lombok Authors.
* Copyright (C) 2013-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -143,13 +143,10 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
job.init(annotation, annInstance, annotationNode);
boolean generateBuilderMethod;
if (job.builderMethodName.isEmpty()) {
generateBuilderMethod = false;
} else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) {
return;
} else {
generateBuilderMethod = true;
}
if (job.builderMethodName.isEmpty()) generateBuilderMethod = false;
else if (!checkName("builderMethodName", job.builderMethodName, annotationNode)) return;
else generateBuilderMethod = true;
if (!checkName("buildMethodName", job.buildMethodName, annotationNode)) return;
// Do not delete the SuperBuilder annotation here, we need it for @Jacksonized.
@@ -313,9 +310,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
if (sd == null) continue;
JavacSingularizer singularizer = sd.getSingularizer();
if (singularizer == null) continue;
if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) {
bfd.singularData = null;
}
if (singularizer.checkForAlreadyExistingNodesAndGenerateError(job.builderAbstractType, sd)) bfd.singularData = null;
}
}
@@ -389,7 +384,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
sanityCheckForMethodGeneratingAnnotationsOnBuilderClass(job.builderImplType, annotationNode);
}
// Create a simple constructor for the BuilderImpl class.
JCMethodDecl cd = HandleConstructor.createConstructor(AccessLevel.PRIVATE, List.<JCAnnotation>nil(), job.builderImplType, List.<JavacNode>nil(), false, annotationNode);
if (cd != null) injectMethod(job.builderImplType, cd);
@@ -515,7 +510,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
recursiveSetGeneratedBy(builder, job.sourceNode);
return injectType(job.parentType, builder);
}
/**
* Generates a constructor that has a builder as the only parameter.
* The values from the builder are used to initialize the fields of new instances.
@@ -582,8 +577,8 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
if (callBuilderBasedSuperConstructor) {
// The first statement must be the call to the super constructor.
JCMethodInvocation callToSuperConstructor = maker.Apply(List.<JCExpression>nil(),
maker.Ident(job.toName("super")),
List.<JCExpression>of(maker.Ident(builderVariableName)));
maker.Ident(job.toName("super")),
List.<JCExpression>of(maker.Ident(builderVariableName)));
statements.prepend(maker.Exec(callToSuperConstructor));
}
@@ -624,7 +619,9 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
/**
* Generates a <code>toBuilder()</code> method in the annotated class that looks like this:
* Generates a {@code toBuilder()} method in the annotated class.
*
* It looks like:
* <pre>
* public ParentBuilder&lt;?, ?&gt; toBuilder() {
* return new <i>Foobar</i>BuilderImpl().$fillValuesFrom(this);
@@ -657,10 +654,11 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
createRelevantNonNullAnnotation(job.parentType, methodDef);
return methodDef;
}
/**
* Generates a <code>$fillValuesFrom()</code> method in the abstract builder class that looks
* like this:
* Generates a {@code $fillValuesFrom()} method in the abstract builder class.
*
* It looks like:
* <pre>
* protected B $fillValuesFrom(final C instance) {
* super.$fillValuesFrom(instance);
@@ -682,7 +680,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JCExpression classGenericNameExpr = maker.Ident(job.toName(classGenericName));
JCVariableDecl param = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(INSTANCE_VARIABLE_NAME), classGenericNameExpr, null);
ListBuffer<JCStatement> body = new ListBuffer<JCStatement>();
if (inherited) {
@@ -706,11 +704,12 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.of(param), List.<JCExpression>nil(), bodyBlock, null);
}
/**
* Generates a <code>$fillValuesFromInstanceIntoBuilder()</code> method in
* Generates a {@code $fillValuesFromInstanceIntoBuilder()} method in
* the builder implementation class that copies all fields from the instance
* to the builder. It looks like this:
* to the builder.
* It looks like:
*
* <pre>
* protected B $fillValuesFromInstanceIntoBuilder(Foobar instance, FoobarBuilder&lt;?, ?&gt; b) {
@@ -727,7 +726,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
// 1st parameter: "Foobar instance"
JCVariableDecl paramInstance = maker.VarDef(maker.Modifiers(Flags.PARAMETER | Flags.FINAL), job.toName(INSTANCE_VARIABLE_NAME), cloneSelfType(job.parentType), null);
// 2nd parameter: "FoobarBuilder<?, ?> b" (plus generics on the annotated type)
// First add all generics that are present on the parent type.
ListBuffer<JCExpression> typeParamsForBuilderParameter = getTypeParamExpressions(job.typeParams, maker, job.sourceNode);
@@ -800,7 +799,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
Name name = job.toName(SELF_METHOD);
JCExpression returnType = maker.Ident(job.toName(builderGenericName));
returnType = addCheckerFrameworkReturnsReceiver(returnType, maker, job.builderType, job.checkerFramework);
return maker.MethodDef(modifiers, name, returnType, List.<JCTypeParameter>nil(), List.<JCVariableDecl>nil(), List.<JCExpression>nil(), null, null);
}
@@ -965,7 +964,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JavacTreeMaker maker = fieldNode.getTreeMaker();
List<JCAnnotation> methodAnns = JavacHandlerUtil.findCopyableToSetterAnnotations(originalFieldNode);
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);
@@ -1046,8 +1045,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
java.util.HashSet<String> usedNames = new HashSet<String>();
// 1. Add type parameter names.
for (JCTypeParameter typeParam : typeParams)
usedNames.add(typeParam.getName().toString());
for (JCTypeParameter typeParam : typeParams) usedNames.add(typeParam.getName().toString());
// 2. Add class name.
usedNames.add(td.name.toString());
@@ -1056,16 +1054,13 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
for (JCTree member : td.getMembers()) {
if (member.getKind() == com.sun.source.tree.Tree.Kind.VARIABLE && member instanceof JCVariableDecl) {
JCTree type = ((JCVariableDecl)member).getType();
if (type instanceof JCIdent)
usedNames.add(((JCIdent)type).getName().toString());
if (type instanceof JCIdent) usedNames.add(((JCIdent)type).getName().toString());
}
}
// 4. Add extends and implements clauses.
addFirstToken(usedNames, Javac.getExtendsClause(td));
for (JCExpression impl : td.getImplementsClause()) {
addFirstToken(usedNames, impl);
}
for (JCExpression impl : td.getImplementsClause()) addFirstToken(usedNames, impl);
return usedNames;
}
@@ -1078,7 +1073,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
while (type instanceof JCFieldAccess && ((JCFieldAccess)type).selected != null) {
// Add the first token, because only that can collide.
type = ((JCFieldAccess)type).selected;
type = ((JCFieldAccess) type).selected;
}
usedNames.add(type.toString());
}
@@ -1118,7 +1113,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
}
return typeParamsForBuilderParameter;
}
private JCExpression copySelect(JavacTreeMaker maker, JCFieldAccess typeParam) {
java.util.List<Name> chainNames = new ArrayList<Name>();
JCExpression expression = typeParam;
@@ -1131,7 +1126,7 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
expression = null;
}
}
Collections.reverse(chainNames);
JCExpression typeParameter = null;
for (Name name : chainNames) {
@@ -1154,18 +1149,16 @@ public class HandleSuperBuilder extends JavacAnnotationHandler<SuperBuilder> {
JCMethodDecl md = (JCMethodDecl) def;
String name = md.name.toString();
boolean matches = name.equals("<init>");
if (isTolerate(type, md))
continue;
if (isTolerate(type, md)) continue;
if (matches && md.params != null && md.params.length() == 1) {
// Cannot use typeMatches() here, because the parameter could be fully-qualified, partially-qualified, or not qualified.
// A string-compare of the last part should work. If it's a false-positive, users could still @Tolerate it.
String typeName = md.params.get(0).getType().toString();
int lastIndexOfDot = typeName.lastIndexOf('.');
if (lastIndexOfDot >= 0) {
typeName = typeName.substring(lastIndexOfDot+1);
typeName = typeName.substring(lastIndexOfDot + 1);
}
if ((builderClassName+"<?, ?>").equals(typeName))
return true;
if ((builderClassName+"<?, ?>").equals(typeName)) return true;
}
}
}
@@ -1757,17 +1757,36 @@ public class JavacHandlerUtil {
}
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the setter.
* Searches the given field node for annotations that are specifically intended to be copied to the getter.
*
* @param forceCopyJacksonAnnotations If {@code true}, always copy the annotations regardless of lombok configuration key {@code lombok.copyJacksonAnnotationsToAccessors}.
*/
public static List<JCAnnotation> findCopyableToSetterAnnotations(JavacNode node) {
return findAnnotationsInList(node, COPY_TO_SETTER_ANNOTATIONS);
public static List<JCAnnotation> findCopyableToGetterAnnotations(JavacNode node, boolean forceCopyJacksonAnnotations) {
if (!forceCopyJacksonAnnotations) {
Boolean copyAnnotations = node.getAst().readConfiguration(ConfigurationKeys.COPY_JACKSON_ANNOTATIONS_TO_ACCESSORS);
if (copyAnnotations == null || !copyAnnotations) return List.nil();
}
return findAnnotationsInList(node, JACKSON_COPY_TO_GETTER_ANNOTATIONS);
}
/**
* Searches the given field node for annotations that are specifically intentioned to be copied to the builder's singular method.
* Searches the given field node for annotations that are specifically intended to be copied to the setter.
*
* @param forceCopyJacksonAnnotations If {@code true}, always copy the annotations regardless of lombok configuration key {@code lombok.copyJacksonAnnotationsToAccessors}.
*/
public static List<JCAnnotation> findCopyableToSetterAnnotations(JavacNode node, boolean forceCopyJacksonAnnotations) {
if (!forceCopyJacksonAnnotations) {
Boolean copyAnnotations = node.getAst().readConfiguration(ConfigurationKeys.COPY_JACKSON_ANNOTATIONS_TO_ACCESSORS);
if (copyAnnotations == null || !copyAnnotations) return List.nil();
}
return findAnnotationsInList(node, JACKSON_COPY_TO_SETTER_ANNOTATIONS);
}
/**
* Searches the given field node for annotations that are specifically intended to be copied to the builder's singular method.
*/
public static List<JCAnnotation> findCopyableToBuilderSingularSetterAnnotations(JavacNode node) {
return findAnnotationsInList(node, COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS);
return findAnnotationsInList(node, JACKSON_COPY_TO_BUILDER_SINGULAR_SETTER_ANNOTATIONS);
}
/**
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015-2021 The Project Lombok Authors.
* Copyright (C) 2015-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -362,7 +362,7 @@ public class JavacSingularsRecipes {
statements.prepend(JavacHandlerUtil.generateNullCheck(maker, null, data.getPluralName(), builderType, "%s cannot be null"));
}
List<JCAnnotation> methodAnnotations = copyAnnotations(findCopyableToSetterAnnotations(data.annotation.up()), maker);
List<JCAnnotation> methodAnnotations = copyAnnotations(findCopyableToSetterAnnotations(data.annotation.up(), true), maker);
finishAndInjectMethod(cfv, maker, returnType, returnStatement, data, builderType, source, deprecate, statements, name, List.of(param), methodAnnotations, access, ignoreNullCollections);
}
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2020-2024 The Project Lombok Authors.
* Copyright (C) 2020-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -78,8 +78,8 @@ public class PatchJavadoc {
}
/**
* The method <code>javadoc2HTML</code> changed 2014-12 to accept an
* additional IJavaElement parameter. To support older versions, try to
* The method {@code javadoc2HTML} changed 2014-12 to accept an
* additional {@code IJavaElement} parameter. To support older versions, try to
* find that one too.
*/
private static class Reflection {
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2023 The Project Lombok Authors.
* Copyright (C) 2014-2025 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -67,14 +67,14 @@ import java.util.zip.ZipInputStream;
* <p>
* Load order is as follows if at least one override is present:
* <li>First, if the resource is found in one of the paths stated in the shadow classpath, find that.
* <li>Next, ask the <code>parent</code> loader, which is passed during construction of the ShadowClassLoader.
* <li>Next, ask the {@code parent} loader, which is passed during construction of the ShadowClassLoader.
* <li>Notably, this jar's contents are always skipped! (The idea of the shadow classpath is that this jar only functions as a launcher, not as a source of your actual application).
* </ul>
*
* If no overrides are present, the load order is as follows:
* <li>First, if the resource is found in our own jar (trying ".SCL.<em>sclSuffix</em>" first for any resource request ending in ".class"), return that.
* <li>Next, check any jar files other than our own, loading them via this classloader, if they have a file <code>META-INF/ShadowClassLoader</code> that contains a line of text with <em>sclSuffix</em>.
* <li>Next, ask the <code>parent</code> loader.
* <li>Next, check any jar files other than our own, loading them via this classloader, if they have a file {@code META-INF/ShadowClassLoader} that contains a line of text with <em>sclSuffix</em>.
* <li>Next, ask the {@code parent} loader.
* </ul>
*
* Use ShadowClassLoader to accomplish the following things:<ul>
@@ -0,0 +1,20 @@
public class JacksonizedAccessors {
@com.fasterxml.jackson.annotation.JsonProperty("intValue")
private int intValue;
@com.fasterxml.jackson.annotation.JsonProperty("intValue")
@java.lang.SuppressWarnings("all")
@lombok.Generated
public int intValue() {
return this.intValue;
}
/**
* @return {@code this}.
*/
@com.fasterxml.jackson.annotation.JsonProperty("intValue")
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonizedAccessors intValue(final int intValue) {
this.intValue = intValue;
return this;
}
}
@@ -5,8 +5,8 @@ import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonBuilderSingular.JacksonBuilderSingularBuilder.class)
public class JacksonBuilderSingular {
@com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder.class)
public class JacksonizedBuilderSingular {
@JsonAnySetter
private Map<String, Object> any;
@JsonProperty("v_a_l_u_e_s")
@@ -17,7 +17,7 @@ public class JacksonBuilderSingular {
private ImmutableList<String> guavaValues;
@java.lang.SuppressWarnings("all")
@lombok.Generated
JacksonBuilderSingular(final Map<String, Object> any, final List<String> values, final ImmutableMap<String, Object> guavaAny, final ImmutableList<String> guavaValues) {
JacksonizedBuilderSingular(final Map<String, Object> any, final List<String> values, final ImmutableMap<String, Object> guavaAny, final ImmutableList<String> guavaValues) {
this.any = any;
this.values = values;
this.guavaAny = guavaAny;
@@ -26,7 +26,7 @@ public class JacksonBuilderSingular {
@java.lang.SuppressWarnings("all")
@lombok.Generated
@com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "", buildMethodName = "build")
public static class JacksonBuilderSingularBuilder {
public static class JacksonizedBuilderSingularBuilder {
@java.lang.SuppressWarnings("all")
@lombok.Generated
private java.util.ArrayList<String> any$key;
@@ -44,12 +44,12 @@ public class JacksonBuilderSingular {
private com.google.common.collect.ImmutableList.Builder<String> guavaValues;
@java.lang.SuppressWarnings("all")
@lombok.Generated
JacksonBuilderSingularBuilder() {
JacksonizedBuilderSingularBuilder() {
}
@JsonAnySetter
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final String anyKey, final Object anyValue) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder any(final String anyKey, final Object anyValue) {
if (this.any$key == null) {
this.any$key = new java.util.ArrayList<String>();
this.any$value = new java.util.ArrayList<Object>();
@@ -60,7 +60,7 @@ public class JacksonBuilderSingular {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final java.util.Map<? extends String, ? extends Object> any) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder any(final java.util.Map<? extends String, ? extends Object> any) {
if (any == null) {
throw new java.lang.NullPointerException("any cannot be null");
}
@@ -76,7 +76,7 @@ public class JacksonBuilderSingular {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearAny() {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearAny() {
if (this.any$key != null) {
this.any$key.clear();
this.any$value.clear();
@@ -85,7 +85,7 @@ public class JacksonBuilderSingular {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder value(final String value) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder value(final String value) {
if (this.values == null) this.values = new java.util.ArrayList<String>();
this.values.add(value);
return this;
@@ -93,7 +93,7 @@ public class JacksonBuilderSingular {
@JsonProperty("v_a_l_u_e_s")
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder values(final java.util.Collection<? extends String> values) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder values(final java.util.Collection<? extends String> values) {
if (values == null) {
throw new java.lang.NullPointerException("values cannot be null");
}
@@ -103,21 +103,21 @@ public class JacksonBuilderSingular {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearValues() {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearValues() {
if (this.values != null) this.values.clear();
return this;
}
@JsonAnySetter
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final String key, final Object value) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaAny(final String key, final Object value) {
if (this.guavaAny == null) this.guavaAny = com.google.common.collect.ImmutableMap.builder();
this.guavaAny.put(key, value);
return this;
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final java.util.Map<? extends String, ? extends Object> guavaAny) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaAny(final java.util.Map<? extends String, ? extends Object> guavaAny) {
if (guavaAny == null) {
throw new java.lang.NullPointerException("guavaAny cannot be null");
}
@@ -127,13 +127,13 @@ public class JacksonBuilderSingular {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaAny() {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearGuavaAny() {
this.guavaAny = null;
return this;
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValue(final String guavaValue) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaValue(final String guavaValue) {
if (this.guavaValues == null) this.guavaValues = com.google.common.collect.ImmutableList.builder();
this.guavaValues.add(guavaValue);
return this;
@@ -141,7 +141,7 @@ public class JacksonBuilderSingular {
@JsonProperty("guava_v_a_l_u_e_s")
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValues(final java.lang.Iterable<? extends String> guavaValues) {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaValues(final java.lang.Iterable<? extends String> guavaValues) {
if (guavaValues == null) {
throw new java.lang.NullPointerException("guavaValues cannot be null");
}
@@ -151,13 +151,13 @@ public class JacksonBuilderSingular {
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaValues() {
public JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearGuavaValues() {
this.guavaValues = null;
return this;
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public JacksonBuilderSingular build() {
public JacksonizedBuilderSingular build() {
java.util.Map<String, Object> any;
switch (this.any$key == null ? 0 : this.any$key.size()) {
case 0:
@@ -184,18 +184,18 @@ public class JacksonBuilderSingular {
}
com.google.common.collect.ImmutableMap<String, Object> guavaAny = this.guavaAny == null ? com.google.common.collect.ImmutableMap.<String, Object>of() : this.guavaAny.build();
com.google.common.collect.ImmutableList<String> guavaValues = this.guavaValues == null ? com.google.common.collect.ImmutableList.<String>of() : this.guavaValues.build();
return new JacksonBuilderSingular(any, values, guavaAny, guavaValues);
return new JacksonizedBuilderSingular(any, values, guavaAny, guavaValues);
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@lombok.Generated
public java.lang.String toString() {
return "JacksonBuilderSingular.JacksonBuilderSingularBuilder(any$key=" + this.any$key + ", any$value=" + this.any$value + ", values=" + this.values + ", guavaAny=" + this.guavaAny + ", guavaValues=" + this.guavaValues + ")";
return "JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder(any$key=" + this.any$key + ", any$value=" + this.any$value + ", values=" + this.values + ", guavaAny=" + this.guavaAny + ", guavaValues=" + this.guavaValues + ")";
}
}
@java.lang.SuppressWarnings("all")
@lombok.Generated
public static JacksonBuilderSingular.JacksonBuilderSingularBuilder builder() {
return new JacksonBuilderSingular.JacksonBuilderSingularBuilder();
public static JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder builder() {
return new JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder();
}
}
}
@@ -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 @com.fasterxml.jackson.annotation.JsonProperty("intValue") @java.lang.SuppressWarnings("all") @lombok.Generated int intValue() {
return this.intValue;
}
/**
* @return {@code this}.
*/
public @com.fasterxml.jackson.annotation.JsonProperty("intValue") @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedAccessors intValue(final int intValue) {
this.intValue = intValue;
return this;
}
}
@@ -7,17 +7,17 @@ import com.google.common.collect.ImmutableMap;
import lombok.Builder;
import lombok.Singular;
import lombok.extern.jackson.Jacksonized;
public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonBuilderSingular.JacksonBuilderSingularBuilder.class) class JacksonBuilderSingular {
public static @java.lang.SuppressWarnings("all") @lombok.Generated @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "",buildMethodName = "build") class JacksonBuilderSingularBuilder {
public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDeserialize(builder = JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder.class) class JacksonizedBuilderSingular {
public static @java.lang.SuppressWarnings("all") @lombok.Generated @com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder(withPrefix = "",buildMethodName = "build") class JacksonizedBuilderSingularBuilder {
private @java.lang.SuppressWarnings("all") @lombok.Generated java.util.ArrayList<String> any$key;
private @java.lang.SuppressWarnings("all") @lombok.Generated java.util.ArrayList<Object> any$value;
private @java.lang.SuppressWarnings("all") @lombok.Generated java.util.ArrayList<String> values;
private @java.lang.SuppressWarnings("all") @lombok.Generated com.google.common.collect.ImmutableMap.Builder<String, Object> guavaAny;
private @java.lang.SuppressWarnings("all") @lombok.Generated com.google.common.collect.ImmutableList.Builder<String> guavaValues;
@java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingularBuilder() {
@java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingularBuilder() {
super();
}
public @JsonAnySetter @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final String anyKey, final Object anyValue) {
public @JsonAnySetter @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder any(final String anyKey, final Object anyValue) {
if ((this.any$key == null))
{
this.any$key = new java.util.ArrayList<String>();
@@ -27,7 +27,7 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
this.any$value.add(anyValue);
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder any(final java.util.Map<? extends String, ? extends Object> any) {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder any(final java.util.Map<? extends String, ? extends Object> any) {
if ((any == null))
{
throw new java.lang.NullPointerException("any cannot be null");
@@ -44,7 +44,7 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
}
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder clearAny() {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearAny() {
if ((this.any$key != null))
{
this.any$key.clear();
@@ -52,13 +52,13 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
}
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder value(final String value) {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder value(final String value) {
if ((this.values == null))
this.values = new java.util.ArrayList<String>();
this.values.add(value);
return this;
}
public @JsonProperty("v_a_l_u_e_s") @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder values(final java.util.Collection<? extends String> values) {
public @JsonProperty("v_a_l_u_e_s") @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder values(final java.util.Collection<? extends String> values) {
if ((values == null))
{
throw new java.lang.NullPointerException("values cannot be null");
@@ -68,18 +68,18 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
this.values.addAll(values);
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder clearValues() {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearValues() {
if ((this.values != null))
this.values.clear();
return this;
}
public @JsonAnySetter @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final String key, final Object value) {
public @JsonAnySetter @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaAny(final String key, final Object value) {
if ((this.guavaAny == null))
this.guavaAny = com.google.common.collect.ImmutableMap.builder();
this.guavaAny.put(key, value);
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaAny(final java.util.Map<? extends String, ? extends Object> guavaAny) {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaAny(final java.util.Map<? extends String, ? extends Object> guavaAny) {
if ((guavaAny == null))
{
throw new java.lang.NullPointerException("guavaAny cannot be null");
@@ -89,17 +89,17 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
this.guavaAny.putAll(guavaAny);
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaAny() {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearGuavaAny() {
this.guavaAny = null;
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValue(final String guavaValue) {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaValue(final String guavaValue) {
if ((this.guavaValues == null))
this.guavaValues = com.google.common.collect.ImmutableList.builder();
this.guavaValues.add(guavaValue);
return this;
}
public @JsonProperty("guava_v_a_l_u_e_s") @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder guavaValues(final java.lang.Iterable<? extends String> guavaValues) {
public @JsonProperty("guava_v_a_l_u_e_s") @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder guavaValues(final java.lang.Iterable<? extends String> guavaValues) {
if ((guavaValues == null))
{
throw new java.lang.NullPointerException("guavaValues cannot be null");
@@ -109,11 +109,11 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
this.guavaValues.addAll(guavaValues);
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder clearGuavaValues() {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder clearGuavaValues() {
this.guavaValues = null;
return this;
}
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular build() {
public @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular build() {
java.util.Map<String, Object> any;
switch (((this.any$key == null) ? 0 : this.any$key.size())) {
case 0 :
@@ -141,24 +141,24 @@ public @Jacksonized @Builder @com.fasterxml.jackson.databind.annotation.JsonDese
}
com.google.common.collect.ImmutableMap<String, Object> guavaAny = ((this.guavaAny == null) ? com.google.common.collect.ImmutableMap.<String, Object>of() : this.guavaAny.build());
com.google.common.collect.ImmutableList<String> guavaValues = ((this.guavaValues == null) ? com.google.common.collect.ImmutableList.<String>of() : this.guavaValues.build());
return new JacksonBuilderSingular(any, values, guavaAny, guavaValues);
return new JacksonizedBuilderSingular(any, values, guavaAny, guavaValues);
}
public @java.lang.Override @java.lang.SuppressWarnings("all") @lombok.Generated java.lang.String toString() {
return (((((((((("JacksonBuilderSingular.JacksonBuilderSingularBuilder(any$key=" + this.any$key) + ", any$value=") + this.any$value) + ", values=") + this.values) + ", guavaAny=") + this.guavaAny) + ", guavaValues=") + this.guavaValues) + ")");
return (((((((((("JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder(any$key=" + this.any$key) + ", any$value=") + this.any$value) + ", values=") + this.values) + ", guavaAny=") + this.guavaAny) + ", guavaValues=") + this.guavaValues) + ")");
}
}
private @JsonAnySetter @Singular("any") Map<String, Object> any;
private @JsonProperty("v_a_l_u_e_s") @Singular List<String> values;
private @JsonAnySetter @Singular("guavaAny") ImmutableMap<String, Object> guavaAny;
private @JsonProperty("guava_v_a_l_u_e_s") @Singular ImmutableList<String> guavaValues;
@java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular(final Map<String, Object> any, final List<String> values, final ImmutableMap<String, Object> guavaAny, final ImmutableList<String> guavaValues) {
@java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular(final Map<String, Object> any, final List<String> values, final ImmutableMap<String, Object> guavaAny, final ImmutableList<String> guavaValues) {
super();
this.any = any;
this.values = values;
this.guavaAny = guavaAny;
this.guavaValues = guavaValues;
}
public static @java.lang.SuppressWarnings("all") @lombok.Generated JacksonBuilderSingular.JacksonBuilderSingularBuilder builder() {
return new JacksonBuilderSingular.JacksonBuilderSingularBuilder();
public static @java.lang.SuppressWarnings("all") @lombok.Generated JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder builder() {
return new JacksonizedBuilderSingular.JacksonizedBuilderSingularBuilder();
}
}
}
@@ -1,3 +1,4 @@
//CONF: lombok.copyJacksonAnnotationsToAccessors = true
//version 8: Jackson deps are at least Java7+.
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
@@ -0,0 +1,7 @@
@lombok.extern.jackson.Jacksonized
@lombok.experimental.Accessors(fluent = true)
@lombok.Getter
@lombok.Setter
public class JacksonizedAccessors {
private int intValue;
}
@@ -13,7 +13,7 @@ import lombok.extern.jackson.Jacksonized;
@Jacksonized
@Builder
public class JacksonBuilderSingular {
public class JacksonizedBuilderSingular {
@JsonAnySetter
@Singular("any")
private Map<String, Object> any;