Skip to content

Builder generator built on top of CodeAPI and Annotation Processing.

License

Notifications You must be signed in to change notification settings

JonathanxD/BuilderGenerator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

BuilderGenerator

Public version of CodeAPI BuilderGen.

BuilderGenerator generates the builder based on the source-code (CodeAPI BuilderGen generates builder based on class-file).

BuilderGenerator can only generate Java builders but supports all languages that generate Java Annotation Processing stubs.

Classes generated by BuilderGenerator does not requires runtime-dependency

How to use

Setup annotation processing

TODO

API

TODO

Builder

BuilderGenerator is designed to generate Builders for immutable objects, base classes must contains an inner class Builder<T, B extends Builder<T, B>>, also we recommend you to have a Builder<BASE_CLASS, ?> builder() method.

Base classes

Base classes are classes that provide getter methods for properties. This class must provide a Builder class that standardize builder methods. Builder methods (with methods) can also provide property options, see @PropertyInfo documentation or Property specification section.

Example of valid base class:

interface Person {
    String getName();
    int getAge();
    
    Builder<Person, ?> builder();
    
    interface Builder<T extends Person, B extends Builder<T, B>> extends com.myproject.BaseBuilder<T, B> {
        
        Builder<T, B> withName(String name);
        Builder<T, B> withAge(int age);
        
        // These methods are optional.
        String getName();
        String getAge();
    }
}

A base Builder class is recommended to provide a build() method.

Note: BuilderGenerator provides a Builder class, but is not recommended to extend this class (to avoid runtime-dependency on BuilderGenerator).

Example of BaseBuilder:

interface BaseBuilder<T, B extends BaseBuilder<T, B>> {
    T build();   
}

Implementation class

Implementation class is a concrete class that implements the base class.

Example:

class PersonImpl implements Person {
    private final String name;
    private final int age;
    
    public PersonImpl(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String getName() {
        return this.name;
    }
    
    @Override
    public int getAge() {
        return this.age;
    }
    
    @Override
    public Builder<Person, ?> builder() {
        throw new UnsupportedOperationException();
    }
}

Generating builder class

To generate Builder class you need to annotate the constructor of the implementation class OR annotate a static factory method.

Annotated constructor example:

class PersonImpl implements Person {
    private final String name;
    private final int age;
    
    @GenBuilder(base = Person.class)
    public PersonImpl(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public String getName() {
        return this.name;
    }
    
    @Override
    public int getAge() {
        return this.age;
    }
    
    @Override
    public Builder<Person, ?> builder() {
        return new PersonBuilder(this);
    }
}

Factory method example:

final class MyFactory {
    @GenBuilder
    public static Person personFactory(String name, int age) {
        return new PersonImpl(name, age);
    }
}

Property specification

BytecodeGenerator provide a way to specify some property options like: value validator, default value provider and nullability.

To specify options for a property you need to annotate the base builder property method.

Example:

interface Person {
    String getName();
    int getAge();
    
    Builder<Person, ?> builder();
    
    interface Builder<T extends Person, B extends Builder<T, B>> extends com.myproject.BaseBuilder<T, B> {
        
        Builder<T, B> withName(String name);
        
        @PropertyInfo(validator = @Validator(@MethodRef(value = Validators.class, name = "positiveInt")))
        Builder<T, B> withAge(int age);
        
        // These methods are optional.
        String getName();
        String getAge();
    }
}

By default, properties value cannot be null.

Optional properties

BuilderGenerator supports Optional properties. Only properties where the getter method in the base class returns a Optional<T> instance are marked as optional property.

Example:

interface Person {
    String getName();
    int getAge();
    Optional<LocalDate> getBirthDate();
    
    Builder<Person, ?> builder();
    
    interface Builder<T extends Person, B extends Builder<T, B>> extends com.myproject.BaseBuilder<T, B> {
        
        Builder<T, B> withName(String name);
        
        @PropertyInfo(validator = @Validator(@MethodRef(value = Validators.class, name = "positiveInt")))
        Builder<T, B> withAge(int age);
        
        // Make sure to accept non-Optional type. If you use Optional<LocalData> BuilderGenerator will not mark the property as 'optional property'.
        Builder<T, B> withBirthDate(LocalDate birthDate);
        
        // These methods are optional.
        String getName();
        String getAge();
        Optional<LocalData> getBirthDate();
    }
}

Features

  • Method Reference Validation
  • Builder inner class validation
  • Default value provider and validator inlining (read @Inline javadoc).

Notes

  • BuilderGenerator does not require a base Builder<T, B extends Builder<T, B>> class, but it is recommended to provide a build() method.
  • BuilderGenerator automatically generate null checks for non-null Object properties