From 875f41bd98d218f04f8f97ba12957b4fd9d8179c Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:24:29 +0200 Subject: [PATCH 01/28] Replacing StringUtils.equalsIgnoreCase by Strings.CI.equals --- .../processor/util/JavaLangAnalyser.java | 4 ++-- .../processor/util/TypeNameAnalyser.java | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java index 1395bca4..b60b0309 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java @@ -57,8 +57,8 @@ private JavaLangAnalyser() {} */ public static boolean isNoMethodOfObjectClass(ExecutableElement mth) { String simpleNameOfParent = mth.getEnclosingElement().getSimpleName().toString(); - return !(StringUtils.equals("java.lang.Object", simpleNameOfParent) - || StringUtils.equals("Object", simpleNameOfParent)); + return !(Strings.CS.equals("java.lang.Object", simpleNameOfParent) + || Strings.CS.equals("Object", simpleNameOfParent)); } /** diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java index 10798226..c1fc2be9 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java @@ -23,7 +23,7 @@ */ package org.javahelpers.simple.builders.processor.util; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.javahelpers.simple.builders.processor.dtos.TypeName; /** Helperclass to extract insights from TypeName. */ @@ -38,8 +38,8 @@ private TypeNameAnalyser() {} * @return {@code true}, if it is a {@code java.util.Map} */ public static boolean isMap(TypeName typeName) { - return StringUtils.equalsIgnoreCase(typeName.getPackageName(), "java.util") - && StringUtils.equalsIgnoreCase(typeName.getClassName(), "Map"); + return Strings.CI.equals(typeName.getPackageName(), "java.util") + && Strings.CI.equals(typeName.getClassName(), "Map"); } /** @@ -49,7 +49,7 @@ public static boolean isMap(TypeName typeName) { * @return {@code true}, if type is a java-base class */ public static boolean isJavaClass(TypeName typeName) { - return StringUtils.equalsAny(typeName.getPackageName(), "java.lang", "java.time", "java.util"); + return Strings.CI.equalsAny(typeName.getPackageName(), "java.lang", "java.time", "java.util"); } /** @@ -59,8 +59,8 @@ public static boolean isJavaClass(TypeName typeName) { * @return {@code true}, if it is a {@code java.util.Set} */ public static boolean isSet(TypeName typeName) { - return StringUtils.equalsIgnoreCase(typeName.getPackageName(), "java.util") - && StringUtils.equalsIgnoreCase(typeName.getClassName(), "Set"); + return Strings.CI.equals(typeName.getPackageName(), "java.util") + && Strings.CI.equals(typeName.getClassName(), "Set"); } /** @@ -70,7 +70,7 @@ public static boolean isSet(TypeName typeName) { * @return {@code true}, if it is a {@code java.util.List} */ public static boolean isList(TypeName typeName) { - return StringUtils.equalsIgnoreCase(typeName.getPackageName(), "java.util") - && StringUtils.equalsIgnoreCase(typeName.getClassName(), "List"); + return Strings.CI.equals(typeName.getPackageName(), "java.util") + && Strings.CI.equals(typeName.getClassName(), "List"); } } From 0aa4827cf22cb8d5d32329b38d4a741cdfa518b5 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:24:54 +0200 Subject: [PATCH 02/28] Solving possible nullpointer-exception from static code analysis --- .../processor/util/BuilderDefinitionCreator.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 301ca5ce..7430d002 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -93,7 +93,6 @@ public static BuilderDefinitionDto extractFromElement( } } - // Todo: moving into helper function List allMembers = elementUtils.getAllMembers(annotatedType); List methods = ElementFilter.methodsIn(allMembers); @@ -132,16 +131,14 @@ && isNotPrivate(mth) private static void addAdditionalHelperMethodsForField( FieldDto result, String fieldName, TypeName fieldType) { - List innerTypes; - int innerTypesCnt; - if (fieldType instanceof TypeNameGeneric fieldTypeGeneric) { - innerTypes = fieldTypeGeneric.getInnerTypeArguments(); - innerTypesCnt = innerTypes.size(); - } else { - innerTypes = null; - innerTypesCnt = 0; + // Only process generic types (List, Set, Map, etc.) + if (!(fieldType instanceof TypeNameGeneric fieldTypeGeneric)) { + return; } + List innerTypes = fieldTypeGeneric.getInnerTypeArguments(); + int innerTypesCnt = innerTypes.size(); + if (isList(fieldType) && innerTypesCnt == 1) { result.addMethod( createFieldSetterWithTransform( From 8c5c4a9ace653980e59a25606382cbc137062014 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:25:10 +0200 Subject: [PATCH 03/28] Adding private constructor for utility-class --- .../builders/processor/util/BuilderDefinitionCreator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 7430d002..0abbb8b0 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -57,6 +57,10 @@ public class BuilderDefinitionCreator { private static final String BUILDER_SUFFIX = "Builder"; + private BuilderDefinitionCreator() { + // Private constructor to prevent instantiation + } + /** * Extracting definition for a builder from annotated element. * From 7bbf6003bf49692b26c492870cd4834f35bf3c37 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:44:06 +0200 Subject: [PATCH 04/28] Fixing codesmells --- .../simple/builders/processor/BuilderProcessor.java | 2 +- .../builders/processor/util/JavaLangAnalyser.java | 2 +- .../builders/processor/util/JavaLangMapper.java | 13 +++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java index 941acebd..1a3f3723 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java @@ -76,7 +76,7 @@ public synchronized void init(ProcessingEnvironment processingEnv) { messager.printMessage( Diagnostic.Kind.ERROR, "simple-builders requires Java 17 or higher for annotation processing. Detected: " - + String.valueOf(current) + + current + ". Please upgrade to JDK 17+ or disable the processor."); } } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java index b60b0309..abb13de7 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java @@ -238,7 +238,7 @@ public static String extractParamJavaDoc(String javaDoc, VariableElement paramet if (line.isEmpty()) { break; } - if (sb.length() > 0) sb.append(' '); + if (!sb.isEmpty()) sb.append(' '); sb.append(line); } else { // TODO: add logging for not found javadoc for parameter diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java index 68c50fe8..75dba739 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java @@ -48,6 +48,11 @@ /** Helper functions to create simple builder types from java.lang types. */ public final class JavaLangMapper { + /** Private constructor to prevent instantiation of utility class. */ + private JavaLangMapper() { + // Utility class + } + /** * Mapper for {@code java.util.Set} to extract the relevant * modifier. If there is a public modifier, this is returned. If there is a protected modifier, @@ -136,7 +141,7 @@ private static TypeName extractType( new SimpleTypeVisitor14() { @Override - public TypeName visitPrimitive(PrimitiveType t, Void _p) { + public TypeName visitPrimitive(PrimitiveType t, Void p) { return switch (t.getKind()) { case BOOLEAN -> TypeNamePrimitive.BOOLEAN; case BYTE -> TypeNamePrimitive.BYTE; @@ -178,19 +183,19 @@ public TypeName visitDeclared(DeclaredType t, Void p) { } @Override - public TypeNameArray visitArray(ArrayType t, Void _p) { + public TypeNameArray visitArray(ArrayType t, Void p) { return new TypeNameArray( extractType(t.getComponentType(), elementUtils, typeUtils), false); } @Override - public TypeName visitTypeVariable(TypeVariable t, Void _p) { + public TypeName visitTypeVariable(TypeVariable t, Void p) { String name = t.asElement().getSimpleName().toString(); return new TypeNameVariable(name); } @Override - protected TypeName defaultAction(TypeMirror e, Void _p) { + protected TypeName defaultAction(TypeMirror e, Void p) { throw new IllegalArgumentException("Unexpected type mirror: " + e); } }, From 376fc93668051bb849dbb30f2f3725b14eb9d5d8 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:44:33 +0200 Subject: [PATCH 05/28] Adding configuration for sonar-server of project --- .sonarlint/connectedMode.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .sonarlint/connectedMode.json diff --git a/.sonarlint/connectedMode.json b/.sonarlint/connectedMode.json new file mode 100644 index 00000000..09f72fd3 --- /dev/null +++ b/.sonarlint/connectedMode.json @@ -0,0 +1,5 @@ +{ + "sonarCloudOrganization": "java-helpers", + "projectKey": "java-helpers_simple-builders", + "region": "EU" +} \ No newline at end of file From 11831b201929b5af12c7f89f01ef7d353898d91f Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:44:57 +0200 Subject: [PATCH 06/28] Solving sonarQube issue --- .../simple/builders/processor/dtos/MethodCodePlaceholder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodePlaceholder.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodePlaceholder.java index 096ee5fa..3d4d069c 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodePlaceholder.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodePlaceholder.java @@ -39,7 +39,7 @@ public abstract class MethodCodePlaceholder { * @param label name of placeholder * @param value dynamic value for placeholder */ - public MethodCodePlaceholder(String label, T value) { + protected MethodCodePlaceholder(String label, T value) { this.label = label; this.value = value; } From 0f8441646ef6b9f428766d9b9bc1c256ff6aef77 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 20:53:17 +0200 Subject: [PATCH 07/28] Replacing raw type by generic --- .../simple/builders/processor/dtos/MethodCodeDto.java | 4 ++-- .../simple/builders/processor/util/JavapoetMapper.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java index 6279c88b..1f825fb0 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java @@ -33,7 +33,7 @@ public class MethodCodeDto { private String codeFormat; /** List of placeholders in CodeFormat. Containing dynamic values too. */ - private final List codeArguments = new ArrayList<>(); + private final List> codeArguments = new ArrayList<>(); /** * Setting format of code. @@ -78,7 +78,7 @@ public String getCodeFormat() { * * @return argument values */ - public List getCodeArguments() { + public List> getCodeArguments() { return codeArguments; } } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavapoetMapper.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavapoetMapper.java index 9d1a9ae6..8b2b704c 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavapoetMapper.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavapoetMapper.java @@ -146,7 +146,7 @@ public static CodeBlock map2CodeBlock( return CodeBlock.builder().addNamed(codeDto.getCodeFormat(), arguments).build(); } - private static Object toCodeblockValue(MethodCodePlaceholder placeHolderValue) { + private static Object toCodeblockValue(MethodCodePlaceholder placeHolderValue) { if (placeHolderValue instanceof MethodCodeStringPlaceholder stringPlaceholder) { return stringPlaceholder.getValue(); } else if (placeHolderValue instanceof MethodCodeTypePlaceholder typePlaceholder) { From 0b4a2b68984f8616d3bd35818c40075d4b118576 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:11:43 +0200 Subject: [PATCH 08/28] Replacing member variable by function parameters --- .../simple/builders/processor/BuilderProcessor.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java index 1a3f3723..c324d50a 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java @@ -29,7 +29,6 @@ import com.google.auto.service.AutoService; import java.util.Set; import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; @@ -55,7 +54,6 @@ public class BuilderProcessor extends AbstractProcessor { private Types typeUtils; private Elements elementUtils; - private Filer filer; private Messager messager; private JavaCodeGenerator codeGenerator; private boolean supportedJdk = true; @@ -65,9 +63,8 @@ public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.typeUtils = processingEnv.getTypeUtils(); this.elementUtils = processingEnv.getElementUtils(); - this.filer = processingEnv.getFiler(); this.messager = processingEnv.getMessager(); - this.codeGenerator = new JavaCodeGenerator(filer); + this.codeGenerator = new JavaCodeGenerator(processingEnv.getFiler()); // Enforce minimum Java version (17+) for the processor SourceVersion current = processingEnv.getSourceVersion(); From bd4900ec2c35f40990d4983f1d05a63df66219e7 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:12:14 +0200 Subject: [PATCH 09/28] Do not return true for processing of annotations, which could not be processed --- .../simple/builders/processor/BuilderProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java index c324d50a..e7ea6e7e 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java @@ -82,7 +82,7 @@ public synchronized void init(ProcessingEnvironment processingEnv) { public boolean process(Set annotations, RoundEnvironment roundEnv) { if (!supportedJdk) { // Fail fast: we already emitted an error in init(); do not attempt any processing. - return true; + return false; } // Resolve annotation as TypeElement to support environments where the Class overload // of getElementsAnnotatedWith is unavailable. @@ -93,7 +93,7 @@ public boolean process(Set annotations, RoundEnvironment if (simpleBuilderAnnotation == null) { // TODO: Logging // Annotation type not on classpath; nothing to do this round. - return true; + return false; } for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(simpleBuilderAnnotation)) { try { From 4cc62c1494e190def8fc07719b8d23b50cebc438 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:12:51 +0200 Subject: [PATCH 10/28] Reducing complexity of extractParamJavaDoc --- .../processor/util/JavaLangAnalyser.java | 109 +++++++++++++----- 1 file changed, 79 insertions(+), 30 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java index abb13de7..af43f560 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java @@ -47,6 +47,8 @@ /** Helperclass for extrating specific information from existing classes. */ public final class JavaLangAnalyser { + private static final String PARAM_TAG = "@param "; + private JavaLangAnalyser() {} /** @@ -209,41 +211,88 @@ public static String extractParamJavaDoc(String javaDoc, VariableElement paramet } String parameterName = parameter.getSimpleName().toString(); String[] lines = javaDoc.split("\r?\n"); - boolean capture = false; + + // Find the @param line for our parameter + int indexOfParamTag = findParamTagLine(lines, parameterName); + if (indexOfParamTag < 0) { + return null; + } + + // Extract text from the @param line and continuation lines + return extractParamText(lines, indexOfParamTag); + } + + /** + * Finds the line index containing @param tag for the given parameter name. + * + * @param lines the javadoc lines + * @param parameterName the parameter to search for + * @return the line index, or -1 if not found + */ + private static int findParamTagLine(String[] lines, String parameterName) { + for (int i = 0; i < lines.length; i++) { + String cleanedLine = cleanJavadocLine(lines[i]); + if (cleanedLine.startsWith(PARAM_TAG)) { + String rest = cleanedLine.substring(PARAM_TAG.length()).trim(); + int spaceIndex = rest.indexOf(' '); + String name = spaceIndex >= 0 ? rest.substring(0, spaceIndex) : rest; + if (Strings.CI.equals(parameterName, name)) { + return i; + } + } + } + return -1; + } + + /** + * Removes leading asterisk and whitespace from a Javadoc line. + * + * @param rawLine the raw line from Javadoc + * @return the cleaned line + */ + private static String cleanJavadocLine(String rawLine) { + String line = rawLine.trim(); + if (line.startsWith("*")) { + return line.substring(1).trim(); + } + return line; + } + + /** + * Extracts the parameter documentation text starting from the @param line. + * + * @param lines the javadoc lines + * @param startIndex the index of the @param line + * @return the extracted documentation text or null if empty + */ + private static String extractParamText(String[] lines, int startIndex) { StringBuilder sb = new StringBuilder(); - for (String rawLine : lines) { - String line = rawLine.trim(); - if (line.startsWith("*")) { - line = line.substring(1).trim(); + + // Extract initial text from the @param line + String firstLine = cleanJavadocLine(lines[startIndex]); + String rest = firstLine.substring(PARAM_TAG.length()).trim(); + int spaceIndex = rest.indexOf(' '); + if (spaceIndex >= 0) { + String initialText = rest.substring(spaceIndex + 1).trim(); + if (!initialText.isEmpty()) { + sb.append(initialText); } - if (line.startsWith("@")) { - // new tag begins; stop capturing unless this is our @param start - capture = false; - if (line.startsWith("@param ")) { - // format: @param text... - String rest = line.substring("@param ".length()).trim(); - int sp = rest.indexOf(' '); - String name = sp >= 0 ? rest.substring(0, sp) : rest; - String text = sp >= 0 ? rest.substring(sp + 1).trim() : ""; - if (Strings.CI.equals(parameterName, name)) { - if (!text.isEmpty()) { - sb.append(text); - } - capture = true; - } - } - continue; + } + + // Append continuation lines until next tag or empty line + for (int i = startIndex + 1; i < lines.length; i++) { + String cleanedLine = cleanJavadocLine(lines[i]); + + if (cleanedLine.isEmpty() || cleanedLine.startsWith("@")) { + break; } - if (capture) { - if (line.isEmpty()) { - break; - } - if (!sb.isEmpty()) sb.append(' '); - sb.append(line); - } else { - // TODO: add logging for not found javadoc for parameter + + if (!sb.isEmpty()) { + sb.append(' '); } + sb.append(cleanedLine); } + String result = sb.toString().trim(); return result.isEmpty() ? null : result; } From 663e5f9f8ed539f46db39e30bc4659c28b8b5fee Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:17:58 +0200 Subject: [PATCH 11/28] Replacing deprecated StringUtils.removeStart by Strings.CI.removeStart --- .../builders/processor/util/BuilderDefinitionCreator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 0abbb8b0..d73298b7 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -44,6 +44,7 @@ import javax.lang.model.util.Types; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.javahelpers.simple.builders.core.annotations.IgnoreInBuilder; import org.javahelpers.simple.builders.core.annotations.SimpleBuilder; import org.javahelpers.simple.builders.core.builders.ArrayListBuilder; @@ -231,7 +232,7 @@ private static void addSupplierMethodsForField( private static Optional createFieldFromSetter( ExecutableElement mth, Elements elementUtils, Types typeUtils) { String methodName = mth.getSimpleName().toString(); - String fieldName = StringUtils.uncapitalize(StringUtils.removeStart(methodName, "set")); + String fieldName = StringUtils.uncapitalize(Strings.CI.removeStart(methodName, "set")); FieldDto result = new FieldDto(); result.setFieldName(fieldName); From 9ad66a3ccb164ff6303a24c8b8eecf2e5ba825db Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:18:26 +0200 Subject: [PATCH 12/28] Replacing rawtype HashSet by diamond HashSet<> --- .../simple/builders/core/builders/HashSetBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java b/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java index 0cc44af9..57448b23 100644 --- a/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java +++ b/core/src/main/java/org/javahelpers/simple/builders/core/builders/HashSetBuilder.java @@ -67,6 +67,6 @@ public HashSetBuilder addAll(Set elements) { * @return a new HashSet containing all elements added to this builder */ public Set build() { - return new HashSet(mSet); + return new HashSet<>(mSet); } } From 4caf95f6cede46b6075598cf69eff3138a45834e Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:18:53 +0200 Subject: [PATCH 13/28] Fixing serialization issue in BuilderException --- .../simple/builders/processor/exceptions/BuilderException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/exceptions/BuilderException.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/exceptions/BuilderException.java index c4eb2036..e7ed1a0a 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/exceptions/BuilderException.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/exceptions/BuilderException.java @@ -28,7 +28,7 @@ /** Special exception for errors in processing of annotations. */ public class BuilderException extends Exception { - private final Element element; + private final transient Element element; /** * Creating an exception with location element, message and parameters. From c1ef825f15fbb69d06cfa28601078d92071f29a5 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:19:19 +0200 Subject: [PATCH 14/28] Replacing repeated literals by constants --- .../util/BuilderDefinitionCreator.java | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index d73298b7..8b570b2a 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -58,6 +58,17 @@ public class BuilderDefinitionCreator { private static final String BUILDER_SUFFIX = "Builder"; + // Template argument keys for code generation + private static final String ARG_FIELD_NAME = "fieldName"; + private static final String ARG_DTO_METHOD_PARAM = "dtoMethodParam"; + private static final String ARG_DTO_METHOD_PARAMS = "dtoMethodParams"; + private static final String ARG_BUILDER_FIELD_WRAPPER = "builderFieldWrapper"; + private static final String ARG_HELPER_TYPE = "helperType"; + + // Parameter name suffixes + private static final String SUFFIX_CONSUMER = "Consumer"; + private static final String SUFFIX_SUPPLIER = "Supplier"; + private BuilderDefinitionCreator() { // Private constructor to prevent instantiation } @@ -339,16 +350,16 @@ private static MethodDto createFieldSetterWithTransform( this.$fieldName:N = $builderFieldWrapper:T.changedValue($dtoMethodParams:N); return this; """); - methodDto.addArgument("fieldName", fieldName); - methodDto.addArgument("dtoMethodParams", params); - methodDto.addArgument("builderFieldWrapper", TypeName.of(TrackedValue.class)); + methodDto.addArgument(ARG_FIELD_NAME, fieldName); + methodDto.addArgument(ARG_DTO_METHOD_PARAMS, params); + methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class)); return methodDto; } private static MethodDto createFieldConsumer(String fieldName, TypeName fieldType) { TypeNameGeneric consumerType = new TypeNameGeneric(map2TypeName(Consumer.class), fieldType); MethodParameterDto parameter = new MethodParameterDto(); - parameter.setParameterName(fieldName + "Consumer"); + parameter.setParameterName(fieldName + SUFFIX_CONSUMER); parameter.setParameterTypeName(consumerType); MethodDto methodDto = new MethodDto(); methodDto.setMethodName(fieldName); @@ -362,10 +373,10 @@ private static MethodDto createFieldConsumer(String fieldName, TypeName fieldTyp this.$fieldName:N = $builderFieldWrapper:T.changedValue(consumer); return this; """); - methodDto.addArgument("fieldName", fieldName); - methodDto.addArgument("dtoMethodParam", parameter.getParameterName()); - methodDto.addArgument("helperType", fieldType); - methodDto.addArgument("builderFieldWrapper", TypeName.of(TrackedValue.class)); + methodDto.addArgument(ARG_FIELD_NAME, fieldName); + methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName()); + methodDto.addArgument(ARG_HELPER_TYPE, fieldType); + methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class)); return methodDto; } @@ -378,7 +389,7 @@ private static MethodDto createFieldConsumerWithBuilder( private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeName builderType) { TypeNameGeneric consumerType = new TypeNameGeneric(map2TypeName(Consumer.class), builderType); MethodParameterDto parameter = new MethodParameterDto(); - parameter.setParameterName(fieldName + BUILDER_SUFFIX + "Consumer"); + parameter.setParameterName(fieldName + BUILDER_SUFFIX + SUFFIX_CONSUMER); parameter.setParameterTypeName(consumerType); MethodDto methodDto = new MethodDto(); methodDto.setMethodName(fieldName); @@ -392,17 +403,17 @@ private static MethodDto createFieldConsumerWithBuilder(String fieldName, TypeNa this.$fieldName:N = $builderFieldWrapper:T.changedValue(builder.build()); return this; """); - methodDto.addArgument("fieldName", fieldName); - methodDto.addArgument("dtoMethodParam", parameter.getParameterName()); - methodDto.addArgument("helperType", builderType); - methodDto.addArgument("builderFieldWrapper", TypeName.of(TrackedValue.class)); + methodDto.addArgument(ARG_FIELD_NAME, fieldName); + methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName()); + methodDto.addArgument(ARG_HELPER_TYPE, builderType); + methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class)); return methodDto; } private static MethodDto createFieldSupplier(String fieldName, TypeName fieldType) { TypeNameGeneric supplierType = new TypeNameGeneric(map2TypeName(Supplier.class), fieldType); MethodParameterDto parameter = new MethodParameterDto(); - parameter.setParameterName(fieldName + "Supplier"); + parameter.setParameterName(fieldName + SUFFIX_SUPPLIER); parameter.setParameterTypeName(supplierType); MethodDto methodDto = new MethodDto(); methodDto.setMethodName(fieldName); @@ -414,9 +425,9 @@ private static MethodDto createFieldSupplier(String fieldName, TypeName fieldTyp this.$fieldName:N = $builderFieldWrapper:T.changedValue($dtoMethodParam:N.get()); return this; """); - methodDto.addArgument("fieldName", fieldName); - methodDto.addArgument("dtoMethodParam", parameter.getParameterName()); - methodDto.addArgument("builderFieldWrapper", TypeName.of(TrackedValue.class)); + methodDto.addArgument(ARG_FIELD_NAME, fieldName); + methodDto.addArgument(ARG_DTO_METHOD_PARAM, parameter.getParameterName()); + methodDto.addArgument(ARG_BUILDER_FIELD_WRAPPER, TypeName.of(TrackedValue.class)); return methodDto; } From 23cbab916c31e3ed6b2497f6184ac01812565fa8 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:27:39 +0200 Subject: [PATCH 15/28] Adding Badges from SonarQube --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 67765c7e..6b74fc7e 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ [![Java Version](https://img.shields.io/badge/Java-17%2B-blue)](https://www.oracle.com/java/technologies/javase/17-relnote-issues.html) [![Maven](https://img.shields.io/badge/Maven-3.8%2B-orange)](https://maven.apache.org/) [![codecov](https://codecov.io/gh/java-helpers/simple-builders/graph/badge.svg)](https://codecov.io/gh/java-helpers/simple-builders) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=java-helpers_simple-builders&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=java-helpers_simple-builders) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=java-helpers_simple-builders&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=java-helpers_simple-builders) ## Table of Contents - [What is Simple Builders?](#what-is-simple-builders) From 37a6b9e096924f6e4f4785f44845fce0234283ff Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:32:27 +0200 Subject: [PATCH 16/28] Using paramized Test instead multiple tests --- .../processor/JavaLangAnalyserTest.java | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java index b9893872..984bb992 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java @@ -5,6 +5,7 @@ import java.lang.annotation.Annotation; import java.util.List; import java.util.Set; +import java.util.stream.Stream; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -15,6 +16,9 @@ import javax.lang.model.type.TypeMirror; import org.javahelpers.simple.builders.processor.util.JavaLangAnalyser; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** Unit tests for {@link JavaLangAnalyser}. */ class JavaLangAnalyserTest { @@ -110,21 +114,30 @@ public A[] getAnnotationsByType(Class annotationType) private final VariableElement parameter = new TestVariableElement("testParam"); - @Test - void extractParamJavaDoc_shouldReturnNull_whenJavaDocIsNull() { - assertNull(JavaLangAnalyser.extractParamJavaDoc(null, parameter)); - } - - @Test - void extractParamJavaDoc_shouldReturnNull_whenParameterIsNull() { - assertNull(JavaLangAnalyser.extractParamJavaDoc("Some Javadoc", null)); - } - - @Test - void extractParamJavaDoc_shouldReturnNull_whenParamTagNotFound() { - String javaDoc = - "/**\n * Some method description.\n * @param otherParam some other parameter\n * @return something\n */"; - assertNull(JavaLangAnalyser.extractParamJavaDoc(javaDoc, parameter)); + private static Stream extractParamJavaDocNullCases() { + VariableElement param = new TestVariableElement("testParam"); + return Stream.of( + Arguments.of("JavaDoc is null", null, param), + Arguments.of("Parameter is null", "Some Javadoc", null), + Arguments.of( + "Param tag not found", + "/**\n * Some method description.\n * @param otherParam some other parameter\n * @return something\n */", + param), + Arguments.of( + "Empty param doc", + "/**\n * Some method description.\n * @param testParam\n * @return something\n */", + param), + Arguments.of( + "Whitespace only param doc", + "/**\n * Some method description.\n * @param testParam \n * @return something\n */", + param)); + } + + @ParameterizedTest(name = "{0}") + @MethodSource("extractParamJavaDocNullCases") + void extractParamJavaDoc_shouldReturnNull( + String testCase, String javaDoc, VariableElement param) { + assertNull(JavaLangAnalyser.extractParamJavaDoc(javaDoc, param)); } @Test @@ -152,20 +165,6 @@ void extractParamJavaDoc_shouldStopAtNextTag() { "this is a test parameter", JavaLangAnalyser.extractParamJavaDoc(javaDoc, parameter)); } - @Test - void extractParamJavaDoc_shouldHandleEmptyParamDoc() { - String javaDoc = - "/**\n * Some method description.\n * @param testParam\n * @return something\n */"; - assertNull(JavaLangAnalyser.extractParamJavaDoc(javaDoc, parameter)); - } - - @Test - void extractParamJavaDoc_shouldHandleWhitespaceOnlyParamDoc() { - String javaDoc = - "/**\n * Some method description.\n * @param testParam \n * @return something\n */"; - assertNull(JavaLangAnalyser.extractParamJavaDoc(javaDoc, parameter)); - } - @Test void extractParamJavaDoc_shouldHandleWindowsLineEndings() { String javaDoc = From 8cf3228af31ccf751733f68278c7adb2b90f65c6 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 21:33:07 +0200 Subject: [PATCH 17/28] Removing protected accessor because this is done in junit jupiter differently now --- .../simple/builders/processor/BuilderProcessorTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index 9a9c28fa..6be3c368 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -19,11 +19,11 @@ /** Tests for the {@link BuilderProcessor} class. */ class BuilderProcessorTest { - protected BuilderProcessor processor; - protected Compiler compiler; + BuilderProcessor processor; + Compiler compiler; @BeforeEach - protected void setUp() { + void setUp() { processor = new BuilderProcessor(); compiler = Compiler.javac().withProcessors(processor); } From 0fca016bc9c77fb9b44723bcdead3bc6387a2822 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 22:22:03 +0200 Subject: [PATCH 18/28] Replacing optional by own wrapper type, because optional should be supported by special helpers --- .../processor/BuilderProcessorTest.java | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index 6be3c368..016cda58 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -1007,51 +1007,57 @@ public void setAge(int age) { } @Test - // TODO: Replace Optional with other generic wrapper type, because for optional there is a - // feature-request void shouldHandleGenericWrapperTypes() { - JavaFileObject optionalWrapperClass = + JavaFileObject wrapperRecord = + ProcessorTestUtils.forSource( + """ + package test; + + public record WrapperRecord(T value) { + } + """); + + JavaFileObject dtoWithWrappedTypes = ProcessorTestUtils.forSource( """ package test; - import java.util.Optional; import java.time.LocalDate; import org.javahelpers.simple.builders.core.annotations.SimpleBuilder; @SimpleBuilder - public class OptionalWrapper { - private Optional name; - private Optional count; - private Optional date; + public class DtoWithWrappedTypes { + private WrapperRecord name; + private WrapperRecord count; + private WrapperRecord date; - public Optional getName() { return name; } - public void setName(Optional name) { this.name = name; } + public WrapperRecord getName() { return name; } + public void setName(WrapperRecord name) { this.name = name; } - public Optional getCount() { return count; } - public void setCount(Optional count) { this.count = count; } + public WrapperRecord getCount() { return count; } + public void setCount(WrapperRecord count) { this.count = count; } - public Optional getDate() { return date; } - public void setDate(Optional date) { this.date = date; } + public WrapperRecord getDate() { return date; } + public void setDate(WrapperRecord date) { this.date = date; } } """); // Test builder generation (without usage) - Compilation compilation = compile(optionalWrapperClass); - String optionalBuilder = loadGeneratedSource(compilation, "OptionalWrapperBuilder"); + Compilation compilation = compile(wrapperRecord, dtoWithWrappedTypes); + String optionalBuilder = loadGeneratedSource(compilation, "DtoWithWrappedTypesBuilder"); // Verify Optional wrapper generates basic methods - assertGenerationSucceeded(compilation, "OptionalWrapperBuilder", optionalBuilder); + assertGenerationSucceeded(compilation, "DtoWithWrappedTypesBuilder", optionalBuilder); ProcessorAsserts.assertContaining( optionalBuilder, - "public static OptionalWrapperBuilder create()", - "public OptionalWrapperBuilder name(Optional name)", - "public OptionalWrapperBuilder count(Optional count)", - "public OptionalWrapperBuilder date(Optional date)", - "public OptionalWrapper build()"); + "public static DtoWithWrappedTypesBuilder create()", + "public DtoWithWrappedTypesBuilder name(WrapperRecord name)", + "public DtoWithWrappedTypesBuilder count(WrapperRecord count)", + "public DtoWithWrappedTypesBuilder date(WrapperRecord date)", + "public DtoWithWrappedTypes build()"); // Verify no additional helper methods are generated for these generic types - // (since they don't match List/Set/Map patterns) + // (since they don't match List/Set/Map/Optional patterns) ProcessorAsserts.assertNotContaining( optionalBuilder, "nameBuilderConsumer", @@ -1063,9 +1069,9 @@ public class OptionalWrapper { // Backing fields present ProcessorAsserts.assertContaining( optionalBuilder, - "private TrackedValue> name = unsetValue();", - "private TrackedValue> count = unsetValue();", - "private TrackedValue> date = unsetValue();"); + "private TrackedValue> name = unsetValue();", + "private TrackedValue> count = unsetValue();", + "private TrackedValue> date = unsetValue();"); } @Test From 09b3a1fdc714e4a9f50c2495ead0b11da3503eb8 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 22:42:03 +0200 Subject: [PATCH 19/28] Integration of messages to be used for logging --- .../builders/processor/BuilderProcessor.java | 16 ++++---- .../processor/util/ProcessingLogger.java | 10 +++++ .../processor/BuilderProcessorTest.java | 41 ++++++------------- 3 files changed, 29 insertions(+), 38 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java index e7ea6e7e..afe78bd4 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java @@ -29,7 +29,6 @@ import com.google.auto.service.AutoService; import java.util.Set; import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; import javax.annotation.processing.RoundEnvironment; @@ -39,10 +38,10 @@ import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; -import javax.tools.Diagnostic; import org.javahelpers.simple.builders.processor.dtos.BuilderDefinitionDto; import org.javahelpers.simple.builders.processor.exceptions.BuilderException; import org.javahelpers.simple.builders.processor.util.JavaCodeGenerator; +import org.javahelpers.simple.builders.processor.util.ProcessingLogger; /** * BuilderProcessor is an annotation processor for execution in generate-sources phase. The @@ -54,8 +53,8 @@ public class BuilderProcessor extends AbstractProcessor { private Types typeUtils; private Elements elementUtils; - private Messager messager; private JavaCodeGenerator codeGenerator; + private ProcessingLogger logger; private boolean supportedJdk = true; @Override @@ -63,15 +62,14 @@ public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.typeUtils = processingEnv.getTypeUtils(); this.elementUtils = processingEnv.getElementUtils(); - this.messager = processingEnv.getMessager(); + this.logger = new ProcessingLogger(processingEnv.getMessager()); this.codeGenerator = new JavaCodeGenerator(processingEnv.getFiler()); // Enforce minimum Java version (17+) for the processor SourceVersion current = processingEnv.getSourceVersion(); this.supportedJdk = isAtLeastJava17(current); if (!this.supportedJdk) { - messager.printMessage( - Diagnostic.Kind.ERROR, + logger.error( "simple-builders requires Java 17 or higher for annotation processing. Detected: " + current + ". Please upgrade to JDK 17+ or disable the processor."); @@ -91,15 +89,15 @@ public boolean process(Set annotations, RoundEnvironment org.javahelpers.simple.builders.core.annotations.SimpleBuilder.class .getCanonicalName()); if (simpleBuilderAnnotation == null) { - // TODO: Logging - // Annotation type not on classpath; nothing to do this round. + logger.error( + "Annotation org.javahelpers.simple.builders.core.annotations.SimpleBuilder is not on classpath. So nothing to do here."); return false; } for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(simpleBuilderAnnotation)) { try { process(annotatedElement); } catch (BuilderException ex) { - // TODO Logging + logger.error(annotatedElement, "Failed to process annotated element: " + ex.getMessage()); } } return true; diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java index 5b6ef3d6..e843d847 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java @@ -58,6 +58,16 @@ public void error(Element e, String message) { messager.printMessage(Diagnostic.Kind.ERROR, message, e); } + /** + * Reports an error with the specified message. The error will be reported to the underlying + * Messager instance. + * + * @param message the error message to be reported + */ + public void error(String message) { + messager.printMessage(Diagnostic.Kind.ERROR, message); + } + /** * Posts an informational note message to the underlying Messager. This is typically used for * non-critical information during processing. diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index 016cda58..8a4fcfef 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -12,22 +12,12 @@ import javax.tools.JavaFileObject; import org.javahelpers.simple.builders.processor.testing.ProcessorAsserts; import org.javahelpers.simple.builders.processor.testing.ProcessorTestUtils; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; /** Tests for the {@link BuilderProcessor} class. */ class BuilderProcessorTest { - BuilderProcessor processor; - Compiler compiler; - - @BeforeEach - void setUp() { - processor = new BuilderProcessor(); - compiler = Compiler.javac().withProcessors(processor); - } - @Test void shouldFailWhenAnnotationPlacedOnInterface() { // Given: @SimpleBuilder used on an interface instead of a class @@ -44,12 +34,10 @@ public interface WrongTargetInterface { void x(); } Compilation compilation = Compiler.javac().withProcessors(new BuilderProcessor()).compile(source); - // Then: processor swallows validation exceptions; assert no builder was generated - assertThat(compilation).succeeded(); - org.junit.jupiter.api.Assertions.assertFalse( - compilation.generatedFiles().stream() - .anyMatch(f -> f.getName().endsWith("test/WrongTargetInterfaceBuilder.java")), - "Builder should not be generated for interface target"); + // Then: compilation should fail with appropriate error message + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("The SimpleBuilder should be annotated on a class or record"); } @Test @@ -68,12 +56,9 @@ public abstract class AbstractAnnotated { } Compilation compilation = Compiler.javac().withProcessors(new BuilderProcessor()).compile(source); - // Then: processor swallows validation exceptions; assert no builder was generated - assertThat(compilation).succeeded(); - org.junit.jupiter.api.Assertions.assertFalse( - compilation.generatedFiles().stream() - .anyMatch(f -> f.getName().endsWith("test/AbstractAnnotatedBuilder.java")), - "Builder should not be generated for abstract class target"); + // Then: compilation should fail with appropriate error message + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("The SimpleBuilder should not be abstract"); } @Test @@ -929,7 +914,7 @@ void shouldGenerateSupplierMethodForField() { } protected Compilation compile(JavaFileObject... sourceFiles) { - return compiler.compile(sourceFiles); + return Compiler.javac().withProcessors(new BuilderProcessor()).compile(sourceFiles); } @Test @@ -2234,12 +2219,10 @@ public class InnerClass { // When Compilation compilation = compile(source); - // Then: compilation succeeds and no builder class with the conflicting name is generated - assertThat(compilation).succeeded(); - org.junit.jupiter.api.Assertions.assertFalse( - compilation.generatedFiles().stream() - .anyMatch(f -> f.getName().endsWith("test/HasInnerClassWithBuilderBuilder.java")), - "Builder should not be generated when nested builder interface is present"); + // Then: compilation should fail with appropriate error message + assertThat(compilation).failed(); + assertThat(compilation) + .hadErrorContaining("The SimpleBuilder should be declared on a top-level class only"); } @Test From 94604af3c9ce2454c2f8f0ec1cdb197b11995a21 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 22:54:03 +0200 Subject: [PATCH 20/28] Refactoring to enclose typeUtils and elementUtils in ProcessingContext and to wrap calls --- .../builders/processor/BuilderProcessor.java | 14 +- .../util/BuilderDefinitionCreator.java | 55 +++---- .../processor/util/JavaLangAnalyser.java | 26 ++-- .../processor/util/JavaLangMapper.java | 37 ++--- .../processor/util/ProcessingContext.java | 138 ++++++++++++++++++ .../processor/JavaLangAnalyserTest.java | 6 +- 6 files changed, 195 insertions(+), 81 deletions(-) create mode 100644 processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java index afe78bd4..18742266 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java @@ -36,11 +36,10 @@ import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; import org.javahelpers.simple.builders.processor.dtos.BuilderDefinitionDto; import org.javahelpers.simple.builders.processor.exceptions.BuilderException; import org.javahelpers.simple.builders.processor.util.JavaCodeGenerator; +import org.javahelpers.simple.builders.processor.util.ProcessingContext; import org.javahelpers.simple.builders.processor.util.ProcessingLogger; /** @@ -51,8 +50,7 @@ @AutoService(Processor.class) @SupportedAnnotationTypes("org.javahelpers.simple.builders.core.annotations.SimpleBuilder") public class BuilderProcessor extends AbstractProcessor { - private Types typeUtils; - private Elements elementUtils; + private ProcessingContext context; private JavaCodeGenerator codeGenerator; private ProcessingLogger logger; private boolean supportedJdk = true; @@ -60,8 +58,8 @@ public class BuilderProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); - this.typeUtils = processingEnv.getTypeUtils(); - this.elementUtils = processingEnv.getElementUtils(); + this.context = + new ProcessingContext(processingEnv.getElementUtils(), processingEnv.getTypeUtils()); this.logger = new ProcessingLogger(processingEnv.getMessager()); this.codeGenerator = new JavaCodeGenerator(processingEnv.getFiler()); @@ -85,7 +83,7 @@ public boolean process(Set annotations, RoundEnvironment // Resolve annotation as TypeElement to support environments where the Class overload // of getElementsAnnotatedWith is unavailable. TypeElement simpleBuilderAnnotation = - elementUtils.getTypeElement( + context.getTypeElement( org.javahelpers.simple.builders.core.annotations.SimpleBuilder.class .getCanonicalName()); if (simpleBuilderAnnotation == null) { @@ -109,7 +107,7 @@ public SourceVersion getSupportedSourceVersion() { } private void process(Element annotatedElement) throws BuilderException { - BuilderDefinitionDto builderDef = extractFromElement(annotatedElement, elementUtils, typeUtils); + BuilderDefinitionDto builderDef = extractFromElement(annotatedElement, context); codeGenerator.generateBuilder(builderDef); } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 8b570b2a..3017bcc7 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -40,8 +40,6 @@ import javax.lang.model.element.*; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; @@ -77,39 +75,36 @@ private BuilderDefinitionCreator() { * Extracting definition for a builder from annotated element. * * @param annotatedElement annotated elment which is target of builder creation - * @param elementUtils {@code javax.lang.model.util.Elements} utils - * @param typeUtils {@code javax.lang.model.util.Types} utils + * @param context processing context with element and type utilities * @return definition of builder * @throws BuilderException if validation or generation failed */ public static BuilderDefinitionDto extractFromElement( - Element annotatedElement, Elements elementUtils, Types typeUtils) throws BuilderException { + Element annotatedElement, ProcessingContext context) throws BuilderException { validateAnnotatedElement(annotatedElement); TypeElement annotatedType = (TypeElement) annotatedElement; BuilderDefinitionDto result = new BuilderDefinitionDto(); - String packageName = elementUtils.getPackageOf(annotatedType).getQualifiedName().toString(); + String packageName = context.getPackageName(annotatedType); String simpleClassName = annotatedType.getSimpleName().toString(); result.setBuilderTypeName(new TypeName(packageName, simpleClassName + BUILDER_SUFFIX)); result.setBuildingTargetTypeName(new TypeName(packageName, simpleClassName)); // Extract generics from the annotated type via mapper (stream-based) - JavaLangMapper.map2GenericParameterDtos(annotatedType, elementUtils, typeUtils) - .forEach(result::addGeneric); + JavaLangMapper.map2GenericParameterDtos(annotatedType, context).forEach(result::addGeneric); // Extract constructors and their parameters (treat as constructor fields) - Optional constructorOpt = - findConstructorForBuilder(annotatedType, elementUtils); + Optional constructorOpt = findConstructorForBuilder(annotatedType, context); if (constructorOpt.isPresent()) { ExecutableElement ctor = constructorOpt.get(); for (VariableElement param : ctor.getParameters()) { Optional fieldFromCtor = - createFieldFromConstructor(annotatedType, param, elementUtils, typeUtils); + createFieldFromConstructor(annotatedType, param, context); fieldFromCtor.ifPresent(result::addFieldInConstructor); } } - List allMembers = elementUtils.getAllMembers(annotatedType); + List allMembers = context.getAllMembers(annotatedType); List methods = ElementFilter.methodsIn(allMembers); // Build a set of constructor field names to avoid duplicates from setters @@ -122,7 +117,7 @@ public static BuilderDefinitionDto extractFromElement( // nur public if (isMethodRelevantForBuilder(mth)) { // Avoid adding setter-derived fields that duplicate constructor params - Optional maybeField = createFieldFromSetter(mth, elementUtils, typeUtils); + Optional maybeField = createFieldFromSetter(mth, context); if (maybeField.isPresent()) { FieldDto field = maybeField.get(); if (!ctorFieldNames.contains(field.getFieldName())) { @@ -179,8 +174,7 @@ private static void addConsumerMethodsForField( TypeName fieldType, VariableElement fieldParameter, TypeElement fieldTypeElement, - Elements elementUtils, - Types typeUtils) { + ProcessingContext context) { // Do not generate supplier methods for generic type variables (e.g., T) if (fieldType instanceof TypeNameVariable) { return; @@ -189,7 +183,7 @@ private static void addConsumerMethodsForField( if (isFunctionalInterface(fieldTypeElement)) { return; } - Optional builderTypeOpt = findBuilderType(fieldParameter, elementUtils, typeUtils); + Optional builderTypeOpt = findBuilderType(fieldParameter, context); if (builderTypeOpt.isPresent()) { TypeName builderType = builderTypeOpt.get(); result.addMethod( @@ -198,7 +192,7 @@ private static void addConsumerMethodsForField( && fieldTypeElement != null && fieldTypeElement.getKind() == javax.lang.model.element.ElementKind.CLASS && !fieldTypeElement.getModifiers().contains(Modifier.ABSTRACT) - && hasEmptyConstructor(fieldTypeElement, elementUtils)) { + && hasEmptyConstructor(fieldTypeElement, context)) { // Only generate a Consumer for concrete classes with an accessible empty constructor result.addMethod(createFieldConsumer(fieldName, fieldType)); } else if (isList(fieldType) @@ -241,7 +235,7 @@ private static void addSupplierMethodsForField( } private static Optional createFieldFromSetter( - ExecutableElement mth, Elements elementUtils, Types typeUtils) { + ExecutableElement mth, ProcessingContext context) { String methodName = mth.getSimpleName().toString(); String fieldName = StringUtils.uncapitalize(Strings.CI.removeStart(methodName, "set")); @@ -255,11 +249,11 @@ private static Optional createFieldFromSetter( } VariableElement fieldParameter = parameters.get(0); TypeMirror fieldTypeMirror = fieldParameter.asType(); - Element rawElement = typeUtils.asElement(fieldTypeMirror); + Element rawElement = context.asElement(fieldTypeMirror); TypeElement fieldTypeElement = rawElement instanceof TypeElement te ? te : null; // Extract only the @param Javadoc for the single setter parameter (if present) - String fullJavaDoc = elementUtils.getDocComment(mth); + String fullJavaDoc = context.getDocComment(mth); String parameterJavaDocExtracted = JavaLangAnalyser.extractParamJavaDoc(fullJavaDoc, fieldParameter); String parameterJavaDoc = @@ -267,8 +261,7 @@ private static Optional createFieldFromSetter( result.setJavaDoc(parameterJavaDoc); // extracting type of field - MethodParameterDto fieldParameterDto = - map2MethodParameter(fieldParameter, elementUtils, typeUtils); + MethodParameterDto fieldParameterDto = map2MethodParameter(fieldParameter, context); if (fieldParameterDto == null) { // TODO: Logging return Optional.empty(); @@ -278,8 +271,7 @@ private static Optional createFieldFromSetter( // Determine if a corresponding getter exists on the DTO and record its name TypeElement dtoType = (TypeElement) mth.getEnclosingElement(); - JavaLangAnalyser.findGetterForField( - dtoType, fieldName, fieldTypeMirror, elementUtils, typeUtils) + JavaLangAnalyser.findGetterForField(dtoType, fieldName, fieldTypeMirror, context) .ifPresent( getterExecutable -> result.setGetterName(getterExecutable.getSimpleName().toString())); @@ -297,7 +289,7 @@ private static Optional createFieldFromSetter( // add consumer/supplier generation via helpers addConsumerMethodsForField( - result, fieldName, fieldType, fieldParameter, fieldTypeElement, elementUtils, typeUtils); + result, fieldName, fieldType, fieldParameter, fieldTypeElement, context); addSupplierMethodsForField(result, fieldName, fieldType, fieldTypeElement); addAdditionalHelperMethodsForField(result, fieldName, fieldType); @@ -309,8 +301,8 @@ private static Optional createFieldFromSetter( * the constructor argument. */ private static Optional createFieldFromConstructor( - TypeElement dtoType, VariableElement param, Elements elementUtils, Types typeUtils) { - MethodParameterDto paramDto = map2MethodParameter(param, elementUtils, typeUtils); + TypeElement dtoType, VariableElement param, ProcessingContext context) { + MethodParameterDto paramDto = map2MethodParameter(param, context); if (paramDto == null) { return Optional.empty(); } @@ -322,7 +314,7 @@ private static Optional createFieldFromConstructor( // Set javadoc (default to field name if no javadoc found) ctorField.setJavaDoc(fieldName); // Find matching getter on the DTO type - JavaLangAnalyser.findGetterForField(dtoType, fieldName, param.asType(), elementUtils, typeUtils) + JavaLangAnalyser.findGetterForField(dtoType, fieldName, param.asType(), context) .ifPresent(getter -> ctorField.setGetterName(getter.getSimpleName().toString())); // Provide a simple setter in builder to set the constructor argument ctorField.addMethod(createFieldSetterWithTransform(fieldName, null, fieldType)); @@ -432,19 +424,18 @@ private static MethodDto createFieldSupplier(String fieldName, TypeName fieldTyp } private static Optional findBuilderType( - VariableElement param, Elements elementUtils, Types typeUtils) { + VariableElement param, ProcessingContext context) { TypeMirror typeOfParameter = param.asType(); if (typeOfParameter.getKind() == ARRAY || typeOfParameter.getKind().isPrimitive()) { return Optional.empty(); } - Element elementOfParameter = typeUtils.asElement(typeOfParameter); + Element elementOfParameter = context.asElement(typeOfParameter); if (elementOfParameter == null) { // Can happen for primitives or certain compiler-internal types; nothing to build return Optional.empty(); } String simpleClassName = elementOfParameter.getSimpleName().toString(); - String packageName = - elementUtils.getPackageOf(elementOfParameter).getQualifiedName().toString(); + String packageName = context.getPackageName(elementOfParameter); Optional foundBuilderAnnotation = findAnnotation(elementOfParameter, SimpleBuilder.class); boolean isClassWithoutGenerics = StringUtils.containsNone(simpleClassName, "<"); diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java index af43f560..009424eb 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java @@ -38,8 +38,6 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.ElementFilter; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.javahelpers.simple.builders.core.annotations.SimpleBuilderConstructor; @@ -124,21 +122,20 @@ public static boolean isNotStatic(ExecutableElement mth) { public static boolean isSetterForField(ExecutableElement mth) { String name = mth.getSimpleName().toString(); return StringUtils.length(name) > 3 - && Strings.CS.startsWith(name, "set") && StringUtils.isAllUpperCase(StringUtils.substring(name, 3, 4)) && mth.getParameters().size() == 1; } /** - * Helper to check if an element has an empty Constructor. + * Check if the class (TypeElement) has an empty constructor. * * @param typeElement - * @param elementUtils + * @param context processing context * @return {@code true}, if the element has an empty constructor */ - public static boolean hasEmptyConstructor(TypeElement typeElement, Elements elementUtils) { + public static boolean hasEmptyConstructor(TypeElement typeElement, ProcessingContext context) { List constructors = - ElementFilter.constructorsIn(elementUtils.getAllMembers(typeElement)); + ElementFilter.constructorsIn(context.getAllMembers(typeElement)); return constructors.stream().anyMatch(c -> c.getParameters().isEmpty()); } @@ -306,30 +303,27 @@ private static String extractParamText(String[] lines, int startIndex) { * @param dtoType the enclosing DTO type element * @param fieldName the field name (uncapitalized) * @param fieldTypeMirror the expected return type of the getter - * @param elementUtils elements utility - * @param typeUtils types utility + * @param context processing context * @return Optional containing the getter ExecutableElement if found */ public static Optional findGetterForField( TypeElement dtoType, String fieldName, TypeMirror fieldTypeMirror, - Elements elementUtils, - Types typeUtils) { + ProcessingContext context) { if (dtoType == null || fieldName == null || fieldTypeMirror == null) { return Optional.empty(); } String cap = StringUtils.capitalize(fieldName); String getterCandidate = "get" + cap; String booleanGetterCandidate = "is" + cap; - List classMethods = - ElementFilter.methodsIn(elementUtils.getAllMembers(dtoType)); + List classMethods = ElementFilter.methodsIn(context.getAllMembers(dtoType)); // Prefer boolean-style getter if present for (ExecutableElement candidate : classMethods) { String name = candidate.getSimpleName().toString(); if ((name.equals(booleanGetterCandidate) || name.equals(getterCandidate)) && candidate.getParameters().isEmpty() - && typeUtils.isSameType(candidate.getReturnType(), fieldTypeMirror)) { + && context.isSameType(candidate.getReturnType(), fieldTypeMirror)) { return Optional.of(candidate); } } @@ -347,9 +341,9 @@ public static Optional findGetterForField( * @return Optional containing the selected constructor, or empty if none suitable */ public static Optional findConstructorForBuilder( - TypeElement annotatedType, Elements elementUtils) { + TypeElement annotatedType, ProcessingContext context) { List ctors = - ElementFilter.constructorsIn(elementUtils.getAllMembers(annotatedType)); + ElementFilter.constructorsIn(context.getAllMembers(annotatedType)); // First, check if any constructor is annotated with @SimpleBuilderConstructor for (ExecutableElement ctor : ctors) { diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java index 75dba739..464b5ce4 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangMapper.java @@ -40,14 +40,11 @@ import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVariable; -import javax.lang.model.util.Elements; import javax.lang.model.util.SimpleTypeVisitor14; -import javax.lang.model.util.Types; import org.javahelpers.simple.builders.processor.dtos.*; /** Helper functions to create simple builder types from java.lang types. */ public final class JavaLangMapper { - /** Private constructor to prevent instantiation of utility class. */ private JavaLangMapper() { // Utility class @@ -82,20 +79,20 @@ public static TypeName map2TypeName(Class clazz) { /** Maps the declared type parameters of the given type element into GenericParameterDto list. */ public static List map2GenericParameterDtos( - TypeElement type, Elements elementUtils, Types typeUtils) { + TypeElement type, ProcessingContext context) { return type.getTypeParameters().stream() - .map(tp -> map2GenericParameterDto(tp, elementUtils, typeUtils)) + .map(tp -> map2GenericParameterDto(tp, context)) .toList(); } /** Maps a single {@code TypeParameterElement} to {@code GenericParameterDto}. */ public static GenericParameterDto map2GenericParameterDto( - TypeParameterElement tp, Elements elementUtils, Types typeUtils) { + TypeParameterElement tp, ProcessingContext context) { GenericParameterDto g = new GenericParameterDto(); g.setName(tp.getSimpleName().toString()); tp.getBounds().stream() .filter(b -> !"java.lang.Object".equals(b.toString())) - .map(b -> extractType(b, elementUtils, typeUtils)) + .map(b -> extractType(b, context)) .forEach(g::addUpperBound); return g; } @@ -105,16 +102,15 @@ public static GenericParameterDto map2GenericParameterDto( * org.javahelpers.simple.builders.processor.dtos.MethodParameterDto}. * * @param param Parameter variable to be mapped - * @param elementUtils Instance of helper for elements - * @param typeUtils Instance of helper for types + * @param context processing context * @return MethodParameterDto holding the information of the param. */ public static MethodParameterDto map2MethodParameter( - VariableElement param, Elements elementUtils, Types typeUtils) { + VariableElement param, ProcessingContext context) { MethodParameterDto result = new MethodParameterDto(); result.setParameterName(param.getSimpleName().toString()); TypeMirror typeMirror = param.asType(); - TypeName typeName = extractType(typeMirror, elementUtils, typeUtils); + TypeName typeName = extractType(typeMirror, context); if (typeName == null) { return null; } @@ -124,19 +120,18 @@ public static MethodParameterDto map2MethodParameter( /** * Maps a list of {@code TypeMirror} to a list of simple-builder {@code TypeName}s using {@link - * #extractType(TypeMirror, Elements, Types)}. + * #extractType(TypeMirror, ProcessingContext)}. */ private static List extractTypeForList( - List typeMirrors, Elements elementUtils, Types typeUtils) { + List typeMirrors, ProcessingContext context) { List result = new ArrayList<>(typeMirrors.size()); for (TypeMirror tm : typeMirrors) { - result.add(extractType(tm, elementUtils, typeUtils)); + result.add(extractType(tm, context)); } return result; } - private static TypeName extractType( - TypeMirror typeOfParameter, Elements elementUtils, Types typeUtils) { + private static TypeName extractType(TypeMirror typeOfParameter, ProcessingContext context) { return typeOfParameter.accept( new SimpleTypeVisitor14() { @@ -157,10 +152,9 @@ public TypeName visitPrimitive(PrimitiveType t, Void p) { @Override public TypeName visitDeclared(DeclaredType t, Void p) { - TypeElement elementOfParameter = (TypeElement) typeUtils.asElement(typeOfParameter); + TypeElement elementOfParameter = (TypeElement) context.asElement(typeOfParameter); String simpleClassName = elementOfParameter.getSimpleName().toString(); - String packageName = - elementUtils.getPackageOf(elementOfParameter).getQualifiedName().toString(); + String packageName = context.getPackageName(elementOfParameter); TypeName rawType = new TypeName(packageName, simpleClassName); TypeMirror enclosingType = t.getEnclosingType(); TypeName enclosing = @@ -176,7 +170,7 @@ public TypeName visitDeclared(DeclaredType t, Void p) { if (typesExtracted.isEmpty()) { return rawType; } else { - List argTypes = extractTypeForList(typesExtracted, elementUtils, typeUtils); + List argTypes = extractTypeForList(typesExtracted, context); // Represent all generics uniformly return new TypeNameGeneric(rawType, argTypes); } @@ -184,8 +178,7 @@ public TypeName visitDeclared(DeclaredType t, Void p) { @Override public TypeNameArray visitArray(ArrayType t, Void p) { - return new TypeNameArray( - extractType(t.getComponentType(), elementUtils, typeUtils), false); + return new TypeNameArray(extractType(t.getComponentType(), context), false); } @Override diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java new file mode 100644 index 00000000..ebb48b52 --- /dev/null +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java @@ -0,0 +1,138 @@ +/* + * MIT License + * + * Copyright (c) 2025 Andreas Igel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.javahelpers.simple.builders.processor.util; + +import java.util.List; +import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +/** + * Context object that wraps Elements and Types utilities from annotation processing, providing + * domain-specific methods for type and element operations. + * + *

This eliminates repetitive parameter passing and provides a cleaner API. + */ +public final class ProcessingContext { + private final Elements elementUtils; + private final Types typeUtils; + + public ProcessingContext(Elements elementUtils, Types typeUtils) { + this.elementUtils = elementUtils; + this.typeUtils = typeUtils; + } + + /** + * Get a type element by its fully qualified class name. + * + * @param qualifiedName the canonical class name (e.g., "java.lang.String") + * @return the type element, or null if not found + */ + public TypeElement getTypeElement(String qualifiedName) { + return elementUtils.getTypeElement(qualifiedName); + } + + /** + * Get the package containing an element. + * + * @param element the element + * @return the package element + */ + public PackageElement getPackageOf(Element element) { + return elementUtils.getPackageOf(element); + } + + /** + * Get the package name of an element. + * + * @param element the element + * @return the qualified package name + */ + public String getPackageName(Element element) { + return elementUtils.getPackageOf(element).getQualifiedName().toString(); + } + + /** + * Get all members of a type, including inherited members. + * + * @param typeElement the type to inspect + * @return list of all members + */ + public List getAllMembers(TypeElement typeElement) { + return elementUtils.getAllMembers(typeElement); + } + + /** + * Get the Javadoc comment for an element. + * + * @param element the element + * @return the doc comment, or null if none + */ + public String getDocComment(Element element) { + return elementUtils.getDocComment(element); + } + + /** + * Convert a type mirror to its corresponding element. + * + * @param typeMirror the type mirror + * @return the element, or null if not representable as an element + */ + public Element asElement(TypeMirror typeMirror) { + return typeUtils.asElement(typeMirror); + } + + /** + * Check if two types are the same type. + * + * @param type1 first type + * @param type2 second type + * @return true if the types are the same + */ + public boolean isSameType(TypeMirror type1, TypeMirror type2) { + return typeUtils.isSameType(type1, type2); + } + + /** + * Get the raw Elements utility for advanced operations. + * + * @return the Elements utility + */ + public Elements elements() { + return elementUtils; + } + + /** + * Get the raw Types utility for advanced operations. + * + * @return the Types utility + */ + public Types types() { + return typeUtils; + } +} diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java index 984bb992..27a10a2f 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/JavaLangAnalyserTest.java @@ -205,16 +205,16 @@ void extractParamJavaDoc_shouldAddSpaceBetweenLines() { @Test void findGetterForField_shouldReturnEmpty_whenDtoTypeIsNull() { - assertTrue(JavaLangAnalyser.findGetterForField(null, "fieldName", null, null, null).isEmpty()); + assertTrue(JavaLangAnalyser.findGetterForField(null, "fieldName", null, null).isEmpty()); } @Test void findGetterForField_shouldReturnEmpty_whenFieldNameIsNull() { - assertTrue(JavaLangAnalyser.findGetterForField(null, null, null, null, null).isEmpty()); + assertTrue(JavaLangAnalyser.findGetterForField(null, null, null, null).isEmpty()); } @Test void findGetterForField_shouldReturnEmpty_whenFieldTypeMirrorIsNull() { - assertTrue(JavaLangAnalyser.findGetterForField(null, "fieldName", null, null, null).isEmpty()); + assertTrue(JavaLangAnalyser.findGetterForField(null, "fieldName", null, null).isEmpty()); } } From 37e56dc4b4d887fa55fad8c514ef8ac2de1da106 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:00:51 +0200 Subject: [PATCH 21/28] Moving logger into ProcessingContext too --- .../builders/processor/BuilderProcessor.java | 12 +++---- .../processor/util/ProcessingContext.java | 34 +++++++++++++------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java index 18742266..d1b38b24 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/BuilderProcessor.java @@ -52,22 +52,22 @@ public class BuilderProcessor extends AbstractProcessor { private ProcessingContext context; private JavaCodeGenerator codeGenerator; - private ProcessingLogger logger; private boolean supportedJdk = true; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); + ProcessingLogger logger = new ProcessingLogger(processingEnv.getMessager()); this.context = - new ProcessingContext(processingEnv.getElementUtils(), processingEnv.getTypeUtils()); - this.logger = new ProcessingLogger(processingEnv.getMessager()); + new ProcessingContext( + processingEnv.getElementUtils(), processingEnv.getTypeUtils(), logger); this.codeGenerator = new JavaCodeGenerator(processingEnv.getFiler()); // Enforce minimum Java version (17+) for the processor SourceVersion current = processingEnv.getSourceVersion(); this.supportedJdk = isAtLeastJava17(current); if (!this.supportedJdk) { - logger.error( + context.error( "simple-builders requires Java 17 or higher for annotation processing. Detected: " + current + ". Please upgrade to JDK 17+ or disable the processor."); @@ -87,7 +87,7 @@ public boolean process(Set annotations, RoundEnvironment org.javahelpers.simple.builders.core.annotations.SimpleBuilder.class .getCanonicalName()); if (simpleBuilderAnnotation == null) { - logger.error( + context.error( "Annotation org.javahelpers.simple.builders.core.annotations.SimpleBuilder is not on classpath. So nothing to do here."); return false; } @@ -95,7 +95,7 @@ public boolean process(Set annotations, RoundEnvironment try { process(annotatedElement); } catch (BuilderException ex) { - logger.error(annotatedElement, "Failed to process annotated element: " + ex.getMessage()); + context.error(annotatedElement, "Failed to process annotated element: " + ex.getMessage()); } } return true; diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java index ebb48b52..8cd526d9 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java @@ -33,18 +33,20 @@ import javax.lang.model.util.Types; /** - * Context object that wraps Elements and Types utilities from annotation processing, providing - * domain-specific methods for type and element operations. + * Context object that wraps Elements, Types, and logging utilities from annotation processing, + * providing domain-specific methods for type and element operations. * *

This eliminates repetitive parameter passing and provides a cleaner API. */ public final class ProcessingContext { private final Elements elementUtils; private final Types typeUtils; + private final ProcessingLogger logger; - public ProcessingContext(Elements elementUtils, Types typeUtils) { + public ProcessingContext(Elements elementUtils, Types typeUtils, ProcessingLogger logger) { this.elementUtils = elementUtils; this.typeUtils = typeUtils; + this.logger = logger; } /** @@ -119,20 +121,30 @@ public boolean isSameType(TypeMirror type1, TypeMirror type2) { } /** - * Get the raw Elements utility for advanced operations. + * Reports an error at the location of the given element. * - * @return the Elements utility + * @param element the element where the error occurred + * @param message the error message */ - public Elements elements() { - return elementUtils; + public void error(Element element, String message) { + logger.error(element, message); } /** - * Get the raw Types utility for advanced operations. + * Reports an error message. * - * @return the Types utility + * @param message the error message */ - public Types types() { - return typeUtils; + public void error(String message) { + logger.error(message); + } + + /** + * Logs an informational message. + * + * @param message the message to log + */ + public void log(String message) { + logger.log(message); } } From 25410ad9220176abe452a0548e7a9bd03d0160e7 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:07:43 +0200 Subject: [PATCH 22/28] Solving todos by adding missing loggins --- .../processor/util/BuilderDefinitionCreator.java | 8 ++++---- .../builders/processor/util/ProcessingContext.java | 10 ++++++++++ .../builders/processor/util/ProcessingLogger.java | 11 +++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index 3017bcc7..d2790f48 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -243,8 +243,8 @@ private static Optional createFieldFromSetter( result.setFieldName(fieldName); List parameters = mth.getParameters(); if (parameters.size() != 1) { - // TODO: Logging - // Sollte eigentlich nie vorkommen, da das vorher raus gefiltert wurde + // Should never happen, just to be sure here + context.warning(mth.getEnclosingElement(), "Setter method " + methodName + " has " + parameters.size() + " parameters, expected 1"); return Optional.empty(); } VariableElement fieldParameter = parameters.get(0); @@ -263,7 +263,7 @@ private static Optional createFieldFromSetter( // extracting type of field MethodParameterDto fieldParameterDto = map2MethodParameter(fieldParameter, context); if (fieldParameterDto == null) { - // TODO: Logging + context.warning(mth.getEnclosingElement(), "Could not extract type of field " + fieldName); return Optional.empty(); } TypeName fieldType = fieldParameterDto.getParameterType(); @@ -280,7 +280,7 @@ private static Optional createFieldFromSetter( // If there are field-specific generics, no field in builder could be generated for it, so it // needs to be ignored if (CollectionUtils.isNotEmpty(mth.getTypeParameters())) { - // TODO: Logging + context.warning(mth.getEnclosingElement(), "Field " + fieldName + " has field-specific generics, so it will be ignored"); return Optional.empty(); } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java index 8cd526d9..467098ba 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java @@ -147,4 +147,14 @@ public void error(String message) { public void log(String message) { logger.log(message); } + + /** + * Reports a warning at the location of the given element with the specified message. + * + * @param element the element where the warning occurred, used for location information + * @param message the warning message to be reported + */ + public void warning(Element element, String message) { + logger.warning(element, message); + } } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java index e843d847..65d8c417 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingLogger.java @@ -77,4 +77,15 @@ public void error(String message) { public void log(String message) { messager.printMessage(Diagnostic.Kind.NOTE, message); } + + /** + * Reports a warning at the location of the given element with the specified message. The warning + * will be reported to the underlying Messager instance. + * + * @param e the element where the warning occurred, used for location information + * @param message the warning message to be reported + */ + public void warning(Element e, String message) { + messager.printMessage(Diagnostic.Kind.WARNING, message, e); + } } From 7bc9283a256947d08116a1ac71e11ebfb76c0159 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:19:16 +0200 Subject: [PATCH 23/28] Fixing asserts to handle compile-errors and warnings now --- .../builders/processor/util/BuilderDefinitionCreator.java | 8 ++++++-- .../simple/builders/processor/BuilderProcessorTest.java | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java index d2790f48..6df46c67 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/BuilderDefinitionCreator.java @@ -244,7 +244,9 @@ private static Optional createFieldFromSetter( List parameters = mth.getParameters(); if (parameters.size() != 1) { // Should never happen, just to be sure here - context.warning(mth.getEnclosingElement(), "Setter method " + methodName + " has " + parameters.size() + " parameters, expected 1"); + context.warning( + mth.getEnclosingElement(), + "Setter method " + methodName + " has " + parameters.size() + " parameters, expected 1"); return Optional.empty(); } VariableElement fieldParameter = parameters.get(0); @@ -280,7 +282,9 @@ private static Optional createFieldFromSetter( // If there are field-specific generics, no field in builder could be generated for it, so it // needs to be ignored if (CollectionUtils.isNotEmpty(mth.getTypeParameters())) { - context.warning(mth.getEnclosingElement(), "Field " + fieldName + " has field-specific generics, so it will be ignored"); + context.warning( + mth.getEnclosingElement(), + "Field " + fieldName + " has field-specific generics, so it will be ignored"); return Optional.empty(); } diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index 8a4fcfef..c36ce8a9 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -2182,7 +2182,13 @@ public void setFieldWithOwnGeneric(F fieldWithOwnGeneri // Then String generatedCode = loadGeneratedSource(compilation, builderClassName); - assertThat(compilation).succeededWithoutWarnings(); + assertThat(compilation).succeeded(); + assertThat(compilation) + .hadWarningContaining( + "Field fieldWithOwnGeneric has field-specific generics, so it will be ignored"); + assertThat(compilation) + .hadWarningContaining( + "Field fieldWithExtendedClassGeneric has field-specific generics, so it will be ignored"); ProcessorAsserts.assertingResult( generatedCode, // name field should be present From de5f670d41f7b03b2b34a14ff71683f70c9d054a Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:19:35 +0200 Subject: [PATCH 24/28] Fixing handling of methods with one parameter to not be recognized to be setter --- .../simple/builders/processor/util/JavaLangAnalyser.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java index 009424eb..7e6259b5 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/JavaLangAnalyser.java @@ -121,7 +121,8 @@ public static boolean isNotStatic(ExecutableElement mth) { */ public static boolean isSetterForField(ExecutableElement mth) { String name = mth.getSimpleName().toString(); - return StringUtils.length(name) > 3 + return name.startsWith("set") + && StringUtils.length(name) > 3 && StringUtils.isAllUpperCase(StringUtils.substring(name, 3, 4)) && mth.getParameters().size() == 1; } From 436e25c5b7de096073426423cc1ee72a3a52eace Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:21:42 +0200 Subject: [PATCH 25/28] Moving java-util package name in a constant --- .../builders/processor/util/TypeNameAnalyser.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java index c1fc2be9..7f3b9391 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/TypeNameAnalyser.java @@ -29,6 +29,8 @@ /** Helperclass to extract insights from TypeName. */ public class TypeNameAnalyser { + private static final String JAVA_UTIL_PACKAGE = "java.util"; + private TypeNameAnalyser() {} /** @@ -38,7 +40,7 @@ private TypeNameAnalyser() {} * @return {@code true}, if it is a {@code java.util.Map} */ public static boolean isMap(TypeName typeName) { - return Strings.CI.equals(typeName.getPackageName(), "java.util") + return Strings.CI.equals(typeName.getPackageName(), JAVA_UTIL_PACKAGE) && Strings.CI.equals(typeName.getClassName(), "Map"); } @@ -49,7 +51,8 @@ public static boolean isMap(TypeName typeName) { * @return {@code true}, if type is a java-base class */ public static boolean isJavaClass(TypeName typeName) { - return Strings.CI.equalsAny(typeName.getPackageName(), "java.lang", "java.time", "java.util"); + return Strings.CI.equalsAny( + typeName.getPackageName(), "java.lang", "java.time", JAVA_UTIL_PACKAGE); } /** @@ -59,7 +62,7 @@ public static boolean isJavaClass(TypeName typeName) { * @return {@code true}, if it is a {@code java.util.Set} */ public static boolean isSet(TypeName typeName) { - return Strings.CI.equals(typeName.getPackageName(), "java.util") + return Strings.CI.equals(typeName.getPackageName(), JAVA_UTIL_PACKAGE) && Strings.CI.equals(typeName.getClassName(), "Set"); } @@ -70,7 +73,7 @@ public static boolean isSet(TypeName typeName) { * @return {@code true}, if it is a {@code java.util.List} */ public static boolean isList(TypeName typeName) { - return Strings.CI.equals(typeName.getPackageName(), "java.util") + return Strings.CI.equals(typeName.getPackageName(), JAVA_UTIL_PACKAGE) && Strings.CI.equals(typeName.getClassName(), "List"); } } From dad754f92e8cdd455b0e14d13553a941a637ca09 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:32:00 +0200 Subject: [PATCH 26/28] Replacing deprecated Compiler.javac() by static import --- .../builders/processor/BuilderProcessorTest.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index c36ce8a9..82dabded 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -1,13 +1,13 @@ package org.javahelpers.simple.builders.processor; import static com.google.testing.compile.CompilationSubject.assertThat; +import static com.google.testing.compile.Compiler.javac; import static org.javahelpers.simple.builders.processor.testing.ProcessorAsserts.assertGenerationSucceeded; import static org.javahelpers.simple.builders.processor.testing.ProcessorAsserts.contains; import static org.javahelpers.simple.builders.processor.testing.ProcessorAsserts.notContains; import static org.javahelpers.simple.builders.processor.testing.ProcessorTestUtils.loadGeneratedSource; import com.google.testing.compile.Compilation; -import com.google.testing.compile.Compiler; import com.google.testing.compile.JavaFileObjects; import javax.tools.JavaFileObject; import org.javahelpers.simple.builders.processor.testing.ProcessorAsserts; @@ -31,8 +31,7 @@ public interface WrongTargetInterface { void x(); } """); // When - Compilation compilation = - Compiler.javac().withProcessors(new BuilderProcessor()).compile(source); + Compilation compilation = javac().withProcessors(new BuilderProcessor()).compile(source); // Then: compilation should fail with appropriate error message assertThat(compilation).failed(); @@ -53,8 +52,7 @@ public abstract class AbstractAnnotated { } """); // When - Compilation compilation = - Compiler.javac().withProcessors(new BuilderProcessor()).compile(source); + Compilation compilation = javac().withProcessors(new BuilderProcessor()).compile(source); // Then: compilation should fail with appropriate error message assertThat(compilation).failed(); @@ -914,7 +912,7 @@ void shouldGenerateSupplierMethodForField() { } protected Compilation compile(JavaFileObject... sourceFiles) { - return Compiler.javac().withProcessors(new BuilderProcessor()).compile(sourceFiles); + return javac().withProcessors(new BuilderProcessor()).compile(sourceFiles); } @Test @@ -931,7 +929,7 @@ public class ForcedOldRelease { public ForcedOldRelease() {} } // When: compile with a lower language level to simulate older Java (no production code change) Compilation compilation = - Compiler.javac() + javac() .withProcessors(new BuilderProcessor()) .withOptions("--release", "11") .compile(source); From 4dcc235b38089dddf67633be23a1dfa79ec64b69 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:35:09 +0200 Subject: [PATCH 27/28] Replacing call to java() by helper-function --- .../simple/builders/processor/BuilderProcessorTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java index 82dabded..836776af 100644 --- a/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java +++ b/processor/src/test/java/org/javahelpers/simple/builders/processor/BuilderProcessorTest.java @@ -31,7 +31,7 @@ public interface WrongTargetInterface { void x(); } """); // When - Compilation compilation = javac().withProcessors(new BuilderProcessor()).compile(source); + Compilation compilation = compile(source); // Then: compilation should fail with appropriate error message assertThat(compilation).failed(); @@ -52,7 +52,7 @@ public abstract class AbstractAnnotated { } """); // When - Compilation compilation = javac().withProcessors(new BuilderProcessor()).compile(source); + Compilation compilation = compile(source); // Then: compilation should fail with appropriate error message assertThat(compilation).failed(); From 1db578c642937c31a1843a6fa45f59a175780f20 Mon Sep 17 00:00:00 2001 From: AndreasIgel Date: Thu, 9 Oct 2025 23:48:24 +0200 Subject: [PATCH 28/28] Suppress warning because there is no better solution there with possibly having different types --- .../simple/builders/processor/dtos/MethodCodeDto.java | 1 + .../simple/builders/processor/util/ProcessingContext.java | 1 + 2 files changed, 2 insertions(+) diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java index 1f825fb0..c765d0bd 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/dtos/MethodCodeDto.java @@ -78,6 +78,7 @@ public String getCodeFormat() { * * @return argument values */ + @SuppressWarnings("java:S1452") public List> getCodeArguments() { return codeArguments; } diff --git a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java index 467098ba..6fe18222 100644 --- a/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java +++ b/processor/src/main/java/org/javahelpers/simple/builders/processor/util/ProcessingContext.java @@ -85,6 +85,7 @@ public String getPackageName(Element element) { * @param typeElement the type to inspect * @return list of all members */ + @SuppressWarnings("java:S1452") public List getAllMembers(TypeElement typeElement) { return elementUtils.getAllMembers(typeElement); }