Skip to content

Commit 94b3fe7

Browse files
committed
initial commit
0 parents  commit 94b3fe7

File tree

21 files changed

+3262
-0
lines changed

21 files changed

+3262
-0
lines changed

.editorconfig

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# EditorConfig is awesome: https://EditorConfig.org
2+
3+
# top-most EditorConfig file
4+
root = true
5+
6+
# Default settings for all files
7+
[*]
8+
charset = utf-8
9+
end_of_line = lf
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true
12+
13+
# Java source files
14+
[*.java]
15+
indent_style = space
16+
indent_size = 4
17+
max_line_length = 120
18+
continuation_indent_size = 8
19+
20+
# XML files (pom.xml, etc.)
21+
[*.xml]
22+
indent_style = space
23+
indent_size = 4
24+
25+
# Properties files
26+
[*.properties]
27+
indent_style = space
28+
indent_size = 4
29+
30+
# Markdown files
31+
[*.md]
32+
indent_style = space
33+
indent_size = 2
34+
trim_trailing_whitespace = false
35+
max_line_length = off
36+
37+
# YAML files
38+
[*.{yml,yaml}]
39+
indent_style = space
40+
indent_size = 2
41+
42+
# JSON files
43+
[*.json]
44+
indent_style = space
45+
indent_size = 2
46+
47+
# Shell scripts
48+
[*.sh]
49+
indent_style = space
50+
indent_size = 2
51+
52+
# Git files
53+
[{.gitignore,.gitattributes}]
54+
indent_style = space
55+
indent_size = 2

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- 'feature/**'
8+
- 'bugfix/**'
9+
pull_request:
10+
branches:
11+
- main
12+
13+
jobs:
14+
build:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Set up JDK 21
21+
uses: actions/setup-java@v4
22+
with:
23+
distribution: 'temurin'
24+
java-version: '21'
25+
26+
- name: Build with Maven Wrapper
27+
run: ./mvnw clean verify

.github/workflows/release.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Manual Release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
new_version:
7+
description: 'New version (e.g., 1.2.0)'
8+
required: true
9+
release_name:
10+
description: 'Release name'
11+
required: true
12+
13+
jobs:
14+
release:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Checkout
18+
uses: actions/checkout@v4
19+
20+
- name: Set up JDK 21
21+
uses: actions/setup-java@v4
22+
with:
23+
distribution: 'temurin'
24+
java-version: '21'
25+
26+
- name: Configure Git
27+
run: |
28+
git config user.name "${{ github.actor }}"
29+
git config user.email "${{ github.actor }}@users.noreply.github.com"
30+
31+
- name: Bump Maven Version
32+
run: |
33+
./mvnw versions:set -DnewVersion=${{ github.event.inputs.new_version }} -DgenerateBackupPoms=false
34+
./mvnw versions:commit
35+
git add pom.xml */pom.xml
36+
git commit -m "Bump version to ${{ github.event.inputs.new_version }}"
37+
git tag v${{ github.event.inputs.new_version }}
38+
git push origin main --tags
39+
40+
- name: Build Project
41+
run: ./mvnw clean package -DskipTests
42+
43+
- name: Create GitHub Release
44+
uses: softprops/action-gh-release@v1
45+
with:
46+
tag_name: v${{ github.event.inputs.new_version }}
47+
name: ${{ github.event.inputs.release_name }}
48+
files: |
49+
fluent-builder-annotations/target/*.jar
50+
fluent-builder-processor/target/*.jar
51+
env:
52+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Compiled class files
2+
*.class
3+
4+
# Log files
5+
*.log
6+
7+
# Package Files
8+
*.jar
9+
*.war
10+
*.nar
11+
*.ear
12+
*.zip
13+
*.tar.gz
14+
*.rar
15+
16+
# Maven
17+
target/
18+
pom.xml.tag
19+
pom.xml.releaseBackup
20+
pom.xml.versionsBackup
21+
pom.xml.next
22+
release.properties
23+
dependency-reduced-pom.xml
24+
buildNumber.properties
25+
.mvn/timing.properties
26+
.mvn/wrapper/maven-wrapper.jar
27+
28+
# IntelliJ IDEA
29+
.idea/
30+
*.iws
31+
*.iml
32+
*.ipr
33+
out/
34+
35+
# Eclipse
36+
.classpath
37+
.project
38+
.settings/
39+
bin/
40+
41+
# NetBeans
42+
nbproject/private/
43+
build/
44+
nbbuild/
45+
dist/
46+
nbdist/
47+
.nb-gradle/
48+
49+
# VS Code
50+
.vscode/
51+
52+
# Operating System Files
53+
.DS_Store
54+
.DS_Store?
55+
._*
56+
.Spotlight-V100
57+
.Trashes
58+
ehthumbs.db
59+
Thumbs.db
60+
61+
# Temporary files
62+
*.swp
63+
*.swo
64+
*~
65+
.tmp/
66+
67+
# AI
68+
.claude
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
wrapperVersion=3.3.4
2+
distributionType=only-script
3+
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip

LICENSE.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 fluent-builder contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
# Fluent Builder Generator
2+
3+
A Java annotation processor library that automatically generates type-safe fluent builders for Java records with mandatory and optional fields.
4+
5+
## Features
6+
7+
- **Type-safe fluent builders**: Generated builders enforce that all mandatory fields must be set before `build()` can be called
8+
- **Flexible field ordering**: Mandatory fields can be set in any order
9+
- **Optional fields**: Non-mandatory fields can be set at any time during the building process
10+
- **Ignored fields**: Fields marked with `@Ignore` are excluded from the builder (useful for computed/internal fields)
11+
- **Compile-time code generation**: Uses annotation processing to generate builders at compile time
12+
- **Works with any number of mandatory fields**: Automatically generates the appropriate builder levels
13+
14+
## Project Structure
15+
16+
```
17+
fluent-builder-parent/
18+
├── fluent-builder-annotations/ # Contains @FluentBuilder and other annotations
19+
├── fluent-builder-processor/ # Annotation processor that generates builder code
20+
└── fluent-builder-example/ # Example usage
21+
```
22+
23+
## Usage
24+
25+
### 1. Add dependencies to your project
26+
27+
```xml
28+
<dependencies>
29+
<dependency>
30+
<groupId>stream.header.fluentbuilder</groupId>
31+
<artifactId>fluent-builder-annotations</artifactId>
32+
<version>1.0-SNAPSHOT</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>stream.header.fluentbuilder</groupId>
36+
<artifactId>fluent-builder-processor</artifactId>
37+
<version>1.0-SNAPSHOT</version>
38+
<scope>provided</scope>
39+
</dependency>
40+
</dependencies>
41+
42+
<build>
43+
<plugins>
44+
<plugin>
45+
<groupId>org.apache.maven.plugins</groupId>
46+
<artifactId>maven-compiler-plugin</artifactId>
47+
<version>3.11.0</version>
48+
<configuration>
49+
<annotationProcessorPaths>
50+
<path>
51+
<groupId>stream.header.fluentbuilder</groupId>
52+
<artifactId>fluent-builder-processor</artifactId>
53+
<version>1.0-SNAPSHOT</version>
54+
</path>
55+
</annotationProcessorPaths>
56+
</configuration>
57+
</plugin>
58+
</plugins>
59+
</build>
60+
```
61+
62+
### 2. Annotate your record
63+
64+
```java
65+
import stream.header.fluentbuilder.FluentBuilder;
66+
import stream.header.fluentbuilder.FluentBuilder.Mandatory;
67+
import stream.header.fluentbuilder.FluentBuilder.Ignore;
68+
69+
@FluentBuilder
70+
public record Person(
71+
@Mandatory String firstName,
72+
@Mandatory String lastName,
73+
@Mandatory int age,
74+
String email,
75+
String phoneNumber,
76+
String address,
77+
@Ignore String internalId // Ignored - not part of builder, will be null
78+
) {
79+
}
80+
```
81+
82+
### 3. Use the generated builder
83+
84+
```java
85+
// All mandatory fields must be set before build() is available
86+
Person person1 = PersonBuilder.builder()
87+
.firstName("John")
88+
.lastName("Doe")
89+
.age(30)
90+
.email("john.doe@example.com")
91+
.phoneNumber("123-456-7890")
92+
.build();
93+
94+
// Mandatory fields can be set in any order
95+
Person person2 = PersonBuilder.builder()
96+
.lastName("Smith")
97+
.email("jane.smith@example.com") // Optional field can be set anytime
98+
.firstName("Jane")
99+
.address("123 Main St")
100+
.age(25)
101+
.build();
102+
103+
// Only mandatory fields
104+
Person person3 = PersonBuilder.builder()
105+
.age(40)
106+
.firstName("Bob")
107+
.lastName("Johnson")
108+
.build();
109+
```
110+
111+
## How It Works
112+
113+
The annotation processor generates a multi-level builder pattern:
114+
115+
1. **OptionalBuilder**: Abstract base class with methods for all optional fields
116+
2. **InitialBuilder**: Starting point - offers methods for each mandatory field
117+
3. **Intermediate Builders**: One for each combination of mandatory fields not yet set
118+
4. **FinalBuilder**: Has all mandatory fields set - only this builder has a `build()` method
119+
120+
This ensures at compile time that you cannot call `build()` until all mandatory fields have been provided.
121+
122+
## Annotations
123+
124+
### @FluentBuilder
125+
Place on a record to generate a fluent builder for it.
126+
127+
### @FluentBuilder.Mandatory
128+
Mark record components that must be set before `build()` can be called. These fields enforce compile-time type safety.
129+
130+
### @FluentBuilder.Ignore
131+
Mark record components to exclude them from the builder entirely. Ignored fields will be set to `null` when the record is constructed. Useful for:
132+
- Internal/computed fields
133+
- Fields set by factory methods
134+
- Metadata fields not relevant during construction
135+
136+
## Example Generated Code
137+
138+
For the `Person` record above, the processor generates a `PersonBuilder` class with multiple nested builder classes. The type system ensures you can only call `build()` after setting all three mandatory fields (firstName, lastName, age).
139+
140+
## Building the Project
141+
142+
```bash
143+
mvn clean install
144+
```
145+
146+
## Running the Example
147+
148+
```bash
149+
cd fluent-builder-example
150+
mvn exec:java -Dexec.mainClass="com.example.Example"
151+
```
152+
153+
## Comparison with Original Manual Implementation
154+
155+
See `src/main/java/Main.java` for the original manual implementation that inspired this library. The annotation processor automates the generation of this pattern for any record.

0 commit comments

Comments
 (0)