Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9ed0558
First implementation of custom formatting
AndreasIgel Aug 28, 2026
1a6a1f7
Moving formatting to a separated SourceFormatter class
AndreasIgel Aug 28, 2026
39bfee4
Extending tests for skipping formatting
AndreasIgel Aug 28, 2026
077f524
Optimizing Formatter code for performance
AndreasIgel Aug 29, 2026
6527fc5
Replacing empty lines inside javadoc with *-lines
AndreasIgel Aug 29, 2026
fedb670
Switching to a FormattingMode (instead of boolean), supporting 3 ways…
AndreasIgel Aug 29, 2026
070b7b5
Improving codequality
AndreasIgel Aug 30, 2026
0d59180
Adding extended tests for it!
AndreasIgel Aug 30, 2026
d201eb3
Refactoring code to have the formatting in Annotation-Properties
AndreasIgel Aug 30, 2026
2f2229d
Improving test coverage
AndreasIgel Aug 30, 2026
3c6b03e
Calculating coverage of core by tests of processor module
AndreasIgel Aug 30, 2026
4bddb60
Improving coverage by extending tests and refactoring of RoasterSourc…
AndreasIgel Aug 30, 2026
d4f855a
Minimal Change to customerDtoBuilder for expectation of processor run
AndreasIgel Aug 30, 2026
6835c96
Improving runner of performance-measurement
AndreasIgel Aug 30, 2026
1f06ce3
Further Changes on Example data
AndreasIgel Aug 30, 2026
1aee672
Improving formatter
AndreasIgel Aug 30, 2026
47f489c
Fixing test expectation
AndreasIgel Aug 30, 2026
4187856
Fixing sonarqube findings
AndreasIgel Aug 30, 2026
e3cbbce
Improving tests by replacing 3 individual javadoc tests by a parameri…
AndreasIgel Aug 30, 2026
a4b14f7
Removing marker inside test-classes
AndreasIgel Sep 1, 2026
8dc2bbb
Improving RoasterSourceFormatterTest
AndreasIgel Sep 1, 2026
8254493
Fixing processing of formattingMode configuration to be handled like …
AndreasIgel Sep 1, 2026
cf25718
Further improvements in configuration processing for formattingMode
AndreasIgel Sep 1, 2026
94b5d33
Adding documentation for configuration processing
AndreasIgel Sep 1, 2026
8ebe557
Improvements in Javadoc
AndreasIgel Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/fork-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: codecov-payload/jacoco.xml
files: codecov-payload/jacoco.xml,codecov-payload/jacoco-aggregate.xml
flags: processor
name: codecov-upload-fork
override_commit: ${{ steps.meta.outputs.pr_head_sha }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/fork-sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ jobs:
mkdir -p core/target/classes processor/target/classes
cp -a "$src/core-classes/." core/target/classes/ 2>/dev/null || true
cp -a "$src/processor-classes/." processor/target/classes/ 2>/dev/null || true
mkdir -p core/target/site/jacoco processor/target/site/jacoco
cp -a "$src/core-jacoco.xml" core/target/site/jacoco/jacoco.xml 2>/dev/null || true
mkdir -p processor/target/site/jacoco processor/target/site/jacoco-aggregate
cp -a "$src/processor-jacoco.xml" processor/target/site/jacoco/jacoco.xml 2>/dev/null || true
cp -a "$src/jacoco-aggregate.xml" processor/target/site/jacoco-aggregate/jacoco.xml 2>/dev/null || true

- name: Publish fork PR analysis to SonarCloud
if: env.SONAR_TOKEN != ''
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ jobs:
mkdir -p sonar-analysis-data
cp -a core/target/classes sonar-analysis-data/core-classes 2>/dev/null || true
cp -a processor/target/classes sonar-analysis-data/processor-classes 2>/dev/null || true
cp -a core/target/site/jacoco/jacoco.xml sonar-analysis-data/core-jacoco.xml 2>/dev/null || true
cp -a processor/target/site/jacoco-aggregate/jacoco.xml sonar-analysis-data/jacoco-aggregate.xml 2>/dev/null || true
cp -a processor/target/site/jacoco/jacoco.xml sonar-analysis-data/processor-jacoco.xml 2>/dev/null || true
{
printf 'pr_number=%s\n' "$PR_NUMBER"
Expand Down Expand Up @@ -163,6 +163,7 @@ jobs:
run: |
mkdir -p codecov-payload/surefire codecov-payload/failsafe
cp -f processor/target/site/jacoco/jacoco.xml codecov-payload/ 2>/dev/null || true
cp -f processor/target/site/jacoco-aggregate/jacoco.xml codecov-payload/jacoco-aggregate.xml 2>/dev/null || true
cp -f processor/target/surefire-reports/*.xml codecov-payload/surefire/ 2>/dev/null || true
cp -f processor/target/failsafe-reports/*.xml codecov-payload/failsafe/ 2>/dev/null || true
{
Expand Down Expand Up @@ -191,13 +192,15 @@ jobs:
fail_ci_if_error: true
verbose: false

- name: Upload coverage to Codecov (processor)
- name: Upload coverage to Codecov (processor + core via aggregate)
# Skip when CODECOV_TOKEN is unavailable (fork PRs) and for any bot-authored PR.
if: always() && env.CODECOV_TOKEN != '' && github.event.pull_request.user.type != 'Bot'
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
fail_ci_if_error: true
files: processor/target/site/jacoco/jacoco.xml
files: |
processor/target/site/jacoco/jacoco.xml
processor/target/site/jacoco-aggregate/jacoco.xml
flags: processor
name: codecov-upload
verbose: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,38 @@
* @return the suffix for setter method names
*/
String setterSuffix() default "";

/**
* Formatting mode for the generated source code. <br>
* Controls how the generated builder source is post-processed for readability.
*
* <p>Accepted values (case-insensitive):
*
* <ul>
* <li>{@code "jdt"} - Full Eclipse JDT formatting (default)
* <li>{@code "lightweight"} - Lightweight cosmetic formatting (tabs to spaces, javadoc fixup,
* blank line collapsing)
* <li>{@code "none"} - No formatting, raw Roaster output
* </ul>
*
* <p>An empty string (the default) means "inherit from compiler argument {@code
* -Asimplebuilder.formattingMode}", which itself defaults to {@code jdt}.
*
* <p>Example:
*
* <pre>{@code
* @SimpleBuilder(options = @SimpleBuilder.Options(
* formattingMode = "lightweight"
* ))
* public class PersonDto { ... }
* }</pre>
*
* Default: "" (empty - inherit from compiler argument) <br>
* Compiler option: -Asimplebuilder.formattingMode
*
* @return the formatting mode for generated source code
*/
String formattingMode() default "";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@
copyTypeAnnotations = OptionState.DISABLED,
implementsBuilderBase = OptionState.DISABLED,
builderSuffix = "Builder",
setterSuffix = ""))
setterSuffix = "",
formattingMode = "lightweight"))
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
@Inherited
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* MIT License
*
* Copyright (c) 2026 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 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.core.enums;

/**
* Enum representing the formatting mode for generated builder source files.
*
* <p>This enum controls how the raw source code produced by Roaster's {@code toUnformattedString()}
* is post-processed before being written to disk.
*
* <p>Compiler option: {@code -Asimplebuilder.formattingMode=JDT|LIGHTWEIGHT|NONE}
*/
public enum FormattingMode {

/**
* Full Eclipse JDT formatter (default). Produces the highest quality output but is the slowest.
*/
JDT("jdt"),

/**
* Lightweight post-processing. Applies minimal cosmetic fixes (tab-to-space conversion, blank
* line collapsing, javadoc asterisk prefixes) at a fraction of the cost of full formatting.
*/
LIGHTWEIGHT("lightweight"),

/** No formatting at all. Returns raw Roaster output without any post-processing. */
NONE("none");

private final String optionValue;

FormattingMode(String optionValue) {
this.optionValue = optionValue;
}

/** Returns the option string used in compiler arguments and annotations. */
public String getOptionValue() {
return optionValue;
}

/**
* Parses a string into a {@link FormattingMode}.
*
* <p>Accepts case-insensitive matching of either the enum name or the option value. Returns
* {@link #JDT} as the default for unrecognized or null input.
*
* @param value the string to parse (e.g., "jdt", "LIGHTWEIGHT", "none")
* @return the matching FormattingMode, or {@link #JDT} if not recognized
*/
public static FormattingMode fromString(String value) {
if (value == null || value.isBlank()) {
return JDT;
}
for (FormattingMode mode : values()) {
if (mode.optionValue.equalsIgnoreCase(value) || mode.name().equalsIgnoreCase(value)) {
return mode;
}
}
return JDT;
}
}
61 changes: 61 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ Simple-builders supports fine-grained configuration through the `@SimpleBuilder.
- [Integration](#integration)
- [Documentation](#documentation)
- [Reliability](#reliability)
- [Debug Logging](#debug-logging)
- [Performance Tracking](#performance-tracking)
- [Performance Optimization](#performance-optimization)
- [Examples](#examples)
- [Minimal Builder](#minimal-builder)
- [Internal API Builder](#internal-api-builder)
Expand Down Expand Up @@ -993,6 +995,55 @@ mvn compile \

**Note**: `performanceTracking` must be enabled for `performanceOutputFile` to have any effect.

---

### Debug Logging

#### `verbose`

**Default**: `false` | **Compiler Option**: `-Asimplebuilder.verbose=true|false`

> **Note**: This is a **processor-level option** only. It cannot be set per-annotation via
> `@SimpleBuilder.Options`.

Enables debug logging during annotation processing, providing detailed tracing of field discovery,
method analysis, and code generation steps. See [DEBUG_LOGGING.md](DEBUG_LOGGING.md) for detailed
information on log levels, output format, and configuration examples.

---

### Performance Optimization

#### `formattingMode`

**Default**: `jdt` | **Compiler Option**: `-Asimplebuilder.formattingMode=JDT|LIGHTWEIGHT|NONE`
| **Annotation Option**: `@SimpleBuilder.Options(formattingMode = "lightweight")`

Controls how generated source code is post-processed before being written to disk:

| Mode | Description | Trade-offs |
|------|-------------|------------|
| `JDT` (default) | Full Eclipse JDT formatter | Highest quality; slowest (~40% of generation time) |
| `LIGHTWEIGHT` | Minimal cosmetic fixes (tabs→spaces, blank line collapse, javadoc prefixes) | Fast; no line wrapping or import ordering |
| `NONE` | Raw Roaster output, no post-processing | Fastest; no indentation, not suitable for committed code |

**Example**:
```bash
# Maven
mvn compile -Dsimplebuilder.formattingMode=lightweight

# Or via compiler arg
-Asimplebuilder.formattingMode=lightweight
```

Per-annotation override:
```java
@SimpleBuilder(options = @SimpleBuilder.Options(
formattingMode = "lightweight"
))
public class PersonDto { ... }
```

## Examples

### Minimal Builder
Expand Down Expand Up @@ -1347,6 +1398,7 @@ methodAccess = AccessModifier.PRIVATE
-Asimplebuilder.generateStringFormatHelpers=ENABLED|DISABLED
-Asimplebuilder.generateAddToCollectionHelpers=ENABLED|DISABLED
-Asimplebuilder.generateUnboxedOptional=ENABLED|DISABLED
-Asimplebuilder.copyTypeAnnotations=ENABLED|DISABLED

# Collection Helpers
-Asimplebuilder.usingArrayListBuilder=ENABLED|DISABLED
Expand All @@ -1363,6 +1415,9 @@ methodAccess = AccessModifier.PRIVATE
-Asimplebuilder.implementsBuilderBase=ENABLED|DISABLED
-Asimplebuilder.usingGeneratedAnnotation=ENABLED|DISABLED
-Asimplebuilder.usingBuilderImplementationAnnotation=ENABLED|DISABLED
-Asimplebuilder.usingJacksonDeserializerAnnotation=ENABLED|DISABLED
-Asimplebuilder.generateJacksonModule=ENABLED|DISABLED
-Asimplebuilder.jacksonModulePackage=com.your.package

# Documentation
-Asimplebuilder.generateJavaDoc=ENABLED|DISABLED
Expand All @@ -1374,9 +1429,15 @@ methodAccess = AccessModifier.PRIVATE
# Reliability
-Asimplebuilder.strict=ENABLED|DISABLED

# Debug Logging
-Asimplebuilder.verbose=true|false

# Performance Tracking
-Asimplebuilder.performanceTracking=true|false
-Asimplebuilder.performanceOutputFile=path/to/report.json

# Performance Optimization
-Asimplebuilder.formattingMode=JDT|LIGHTWEIGHT|NONE
```

### Complete Options Example
Expand Down
9 changes: 9 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@ After making code changes:
- Changes affecting generation: `mvn test -pl processor,example -am`
3. **Full validation before committing**: `mvn clean test`

### Adding or Modifying Configuration Options

Configuration options flow through a three-layer merge chain: built-in defaults
(`BuilderConfiguration.DEFAULT`), compiler arguments
(`CompilerArgumentsReader.readBuilderConfiguration()`), and annotation values
(`@SimpleBuilder.Options(...)`). Every option must be wired through all three layers.
See the comment in `CompilerArgumentsReader.readBuilderConfiguration()` for the detailed
checklist of files to touch.

### Test Assertions Best Practices

- **Use explicit string literals** for expected values, not variables
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.javahelpers.simple.builders.core.util.BuilderToStringStyle;
import org.javahelpers.simple.builders.core.util.TrackedValue;

public class CustomerDtoBuilder {

private TrackedValue<String> email = unsetValue();
Expand Down Expand Up @@ -75,9 +74,6 @@ public CustomerDto build() {
@Override
public String toString() {
return new ToStringBuilder(this, BuilderToStringStyle.INSTANCE).append("email", this.email)
.append("id", this.id)
.append("name", this.name)
.append("tags", this.tags)
.toString();
.append("id", this.id).append("name", this.name).append("tags", this.tags).toString();
}
}
3 changes: 3 additions & 0 deletions performance-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<!-- Performance tracking disabled by default; enable with -Asimplebuilder.performanceTracking=true -->
<simplebuilder.performanceTracking>false</simplebuilder.performanceTracking>
<simplebuilder.performanceOutputFile></simplebuilder.performanceOutputFile>
<simplebuilder.formattingMode>jdt</simplebuilder.formattingMode>

<plugin.maven.compiler.version>3.15.0</plugin.maven.compiler.version>
<plugin.maven.deploy.version>3.1.4</plugin.maven.deploy.version>
Expand Down Expand Up @@ -117,6 +118,7 @@
<compilerArgs>
<arg>-Asimplebuilder.performanceTracking=${simplebuilder.performanceTracking}</arg>
<arg>-Asimplebuilder.performanceOutputFile=${simplebuilder.performanceOutputFile}</arg>
<arg>-Asimplebuilder.formattingMode=${simplebuilder.formattingMode}</arg>
</compilerArgs>
</configuration>
</execution>
Expand Down Expand Up @@ -156,6 +158,7 @@
<compilerArgs>
<arg>-Asimplebuilder.performanceTracking=${simplebuilder.performanceTracking}</arg>
<arg>-Asimplebuilder.performanceOutputFile=${simplebuilder.performanceOutputFile}</arg>
<arg>-Asimplebuilder.formattingMode=${simplebuilder.formattingMode}</arg>
</compilerArgs>
</configuration>
</execution>
Expand Down
4 changes: 0 additions & 4 deletions performance-test/scripts/generate_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,6 @@ def main(argv: list[str] | None = None) -> int:
errors.append(msg)
print(f" ERROR: {msg}", file=sys.stderr)
continue
print(f" wrote: {out_file}")
written += 1

# --- Generate base classes ---
Expand Down Expand Up @@ -975,7 +974,6 @@ def main(argv: list[str] | None = None) -> int:
else:
try:
atomic_write(out_file, source)
print(f" wrote: {out_file}")
written += 1
except OSError as e:
msg = f"failed to write {out_file}: {e}"
Expand Down Expand Up @@ -1015,7 +1013,6 @@ def main(argv: list[str] | None = None) -> int:
errors.append(msg)
print(f" ERROR: {msg}", file=sys.stderr)
continue
print(f" wrote: {out_file}")
written += 1

# --- Generate DTO classes/records ---
Expand Down Expand Up @@ -1103,7 +1100,6 @@ def main(argv: list[str] | None = None) -> int:
print(f" ERROR: {msg}", file=sys.stderr)
continue

print(f" wrote: {out_file}")
written += 1

print()
Expand Down
Loading
Loading