View on GitHub

FROPOREC - From POJOs to Records

Easy migration from POJOs to Records with Froporec annotation processor

Froporec is a Java Annotation Processor, requiring a minimum of Java 17 and providing annotations which can be used to:

As of v1.4 Froporec also provides a bunch of “convenient” Factory Methods which are really helpful while handling the creation or use of several instances of the same Record class within the same program.



Froporec 1.4 released:

  • Added 5 Static Factory Methods and 2 Instance Factory Methods to all generated Record classes (except for SuperRecord classes).
    • The generated static factory methods are convenient for creating new instances of the generated Record class, with data from either instances of the POJO (or Record) class being converted, or instances of the Record class being generated, with the possibility of ‘overriding’ the instances fields values by combining with the use of a Map of custom values for each field.
    • The generated instance factory methods are convenient for creating new instances of the generated Record class, with data from the current instance, and with the possibility of ‘overriding’ any field value by providing custom values for the desired fields.
  • Added Constants Declarations for fields names, in all generated Record classes (except for SuperRecord classes). Each constant is a String literal with its value being the name of one of the fields of the generated Record class. They are used by the generated factory methods, and can also be accessed from anywhere in your project.
  • Major improvement of collections handling.
  • Minor bug fixes.


Videos

Froporec Java Library (April 2023 Presentation): https://youtu.be/oiW8qLifdIQ

v1.4 - Factory Methods: https://youtu.be/JuCiI4M86CM

Code Migration to Java 17 using FROPOREC and JISEL: https://youtu.be/iML8EjMIDLc

v1.3: https://youtu.be/Gzv65UmWmzw

v1.2 Quick Intro: https://youtu.be/Yu3bR8ZkpYE

Project’s Pitch (v1.0): https://youtu.be/IC0aS_biaMs


Installation

If you are running a Maven project, add the latest release dependency to your pom.xml

<dependency>
    <groupId>org.froporec</groupId>
    <artifactId>froporec</artifactId>
    <version>1.4</version>
</dependency>

You will also need to include the same dependency as an additional annotation processor in the Maven Compiler plugin of your project

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven-compiler-plugin.version}</version>
                <configuration>
                    <release>17</release>
                    <compilerArgs>-Xlint:unchecked</compilerArgs>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.froporec</groupId>
                            <artifactId>froporec</artifactId>
                            <version>1.4</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

For other build tools, please check: Maven Central.


Javadoc

https://javadoc.io/doc/org.froporec/froporec/latest/froporec


Provided Annotations


@Record


@Immutable


@SuperRecord

To be used only on top of either POJO or Record classes.
As a result, a record class with the name pojo_or_record_class_name + SuperRecord will be generated and all fields from the list of Pojo and/or Record classes provided in the mandatory mergeWith attribute, will be added to the fields list of the annotated POJO or Record class:

@SuperRecord(mergeWith = { Pojo1.class, Record1.class, Pojo2.class,... })
public class PojoA {
    // class content...
}

Important Note: the annotation should be used ONLY on POJO or Record classes created in your own project. Any other types are not supported.


The alsoConvert attribute available for both @Record and @Immutable, allows specifying additional types to be transformed into their deeply immutable equivalent. The provided alsoConvert array value may contain a mix of your existing Records or POJOs .class values.

The superInterfaces attribute available for all annotations, allows specifying a list of interfaces to be implemented by the generated Record class.



Constants Declarations for Fields Names

For all generated Record classes (except for SuperRecord), constants declarations are added within the Record class body.

Each constant is a String literal with its value being the name of one of the fields of the generated Record class. They are used by the generated factory methods, and can also be accessed from anywhere in your project.

Below sample code shows the constants declarations added to the generated ImmutableExamReport Record class:

public record ImmutableExamReport(int candidateId, java.lang.String fullName, com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo contactInfo, java.lang.Integer examId, java.lang.String submittedExamContent, java.time.LocalDate examDate, java.lang.Double score, java.lang.Boolean passed) {

    public static final String CANDIDATE_ID = "candidateId"; // type: int
    public static final String FULL_NAME = "fullName"; // type: java.lang.String
    public static final String CONTACT_INFO = "contactInfo"; // type: com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo
    public static final String EXAM_ID = "examId"; // type: java.lang.Integer
    public static final String SUBMITTED_EXAM_CONTENT = "submittedExamContent"; // type: java.lang.String
    public static final String EXAM_DATE = "examDate"; // type: java.time.LocalDate
    public static final String SCORE = "score"; // type: java.lang.Double
    public static final String PASSED = "passed"; // type: java.lang.Boolean
    
    // custom constructor and factory methods follow...
    // ...
}



Factory Methods

For all generated Record classes (except for SuperRecord), factory methods are added within the body of the classes.

The generated static factory methods are convenient for creating new instances of the generated Record class, with data from either instances of the POJO (or Record) class being converted, or instances of the Record class being generated, with the possibility of ‘overriding’ the instances fields values by combining with the use of a Map of custom values for each field.

Below sample code shows the 5 static factory methods added to the generated ImmutableExamReport Record class:

public static ImmutableExamReport buildWith(com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ExamReport examReport) {
    return new ImmutableExamReport(examReport.candidateId(), examReport.fullName(), examReport.contactInfo(), examReport.examId(), examReport.submittedExamContent(), examReport.examDate(), examReport.score(), examReport.passed());
}

public static ImmutableExamReport buildWith(com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ImmutableExamReport immutableExamReport) {
    return new ImmutableExamReport(immutableExamReport.candidateId(), immutableExamReport.fullName(), immutableExamReport.contactInfo(), immutableExamReport.examId(), immutableExamReport.submittedExamContent(), immutableExamReport.examDate(), immutableExamReport.score(), immutableExamReport.passed());
}

@java.lang.SuppressWarnings("unchecked")
public static ImmutableExamReport buildWith(java.util.Map<String, Object> fieldsNameValuePairs) {
    return new ImmutableExamReport((int) fieldsNameValuePairs.getOrDefault(CANDIDATE_ID, 0), (java.lang.String) fieldsNameValuePairs.getOrDefault(FULL_NAME, null), (com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo) fieldsNameValuePairs.getOrDefault(CONTACT_INFO, null), (java.lang.Integer) fieldsNameValuePairs.getOrDefault(EXAM_ID, null), (java.lang.String) fieldsNameValuePairs.getOrDefault(SUBMITTED_EXAM_CONTENT, null), (java.time.LocalDate) fieldsNameValuePairs.getOrDefault(EXAM_DATE, null), (java.lang.Double) fieldsNameValuePairs.getOrDefault(SCORE, null), (java.lang.Boolean) fieldsNameValuePairs.getOrDefault(PASSED, null));
}

@java.lang.SuppressWarnings("unchecked")
public static ImmutableExamReport buildWith(com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ExamReport examReport, java.util.Map<String, Object> fieldsNameValuePairs) {
    return new ImmutableExamReport((int) fieldsNameValuePairs.getOrDefault(CANDIDATE_ID, examReport.candidateId()), (java.lang.String) fieldsNameValuePairs.getOrDefault(FULL_NAME, examReport.fullName()), (com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo) fieldsNameValuePairs.getOrDefault(CONTACT_INFO, examReport.contactInfo()), (java.lang.Integer) fieldsNameValuePairs.getOrDefault(EXAM_ID, examReport.examId()), (java.lang.String) fieldsNameValuePairs.getOrDefault(SUBMITTED_EXAM_CONTENT, examReport.submittedExamContent()), (java.time.LocalDate) fieldsNameValuePairs.getOrDefault(EXAM_DATE, examReport.examDate()), (java.lang.Double) fieldsNameValuePairs.getOrDefault(SCORE, examReport.score()), (java.lang.Boolean) fieldsNameValuePairs.getOrDefault(PASSED, examReport.passed()));
}

@java.lang.SuppressWarnings("unchecked")
public static ImmutableExamReport buildWith(com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ImmutableExamReport immutableExamReport, java.util.Map<String, Object> fieldsNameValuePairs) {
    return new ImmutableExamReport((int) fieldsNameValuePairs.getOrDefault(CANDIDATE_ID, immutableExamReport.candidateId()), (java.lang.String) fieldsNameValuePairs.getOrDefault(FULL_NAME, immutableExamReport.fullName()), (com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo) fieldsNameValuePairs.getOrDefault(CONTACT_INFO, immutableExamReport.contactInfo()), (java.lang.Integer) fieldsNameValuePairs.getOrDefault(EXAM_ID, immutableExamReport.examId()), (java.lang.String) fieldsNameValuePairs.getOrDefault(SUBMITTED_EXAM_CONTENT, immutableExamReport.submittedExamContent()), (java.time.LocalDate) fieldsNameValuePairs.getOrDefault(EXAM_DATE, immutableExamReport.examDate()), (java.lang.Double) fieldsNameValuePairs.getOrDefault(SCORE, immutableExamReport.score()), (java.lang.Boolean) fieldsNameValuePairs.getOrDefault(PASSED, immutableExamReport.passed()));
}


The generated instance factory methods are convenient for creating new instances of the generated Record class, with data from the current instance, and with the possibility of ‘overriding’ any field value by providing custom values for the desired fields.

Below sample code shows the 2 instance factory methods added to the generated ImmutableExamReport Record class:

@java.lang.SuppressWarnings("unchecked")
public ImmutableExamReport with(java.util.Map<String, Object> fieldsNameValuePairs) {
    return new ImmutableExamReport((int) fieldsNameValuePairs.getOrDefault(CANDIDATE_ID, this.candidateId()), (java.lang.String) fieldsNameValuePairs.getOrDefault(FULL_NAME, this.fullName()), (com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo) fieldsNameValuePairs.getOrDefault(CONTACT_INFO, this.contactInfo()), (java.lang.Integer) fieldsNameValuePairs.getOrDefault(EXAM_ID, this.examId()), (java.lang.String) fieldsNameValuePairs.getOrDefault(SUBMITTED_EXAM_CONTENT, this.submittedExamContent()), (java.time.LocalDate) fieldsNameValuePairs.getOrDefault(EXAM_DATE, this.examDate()), (java.lang.Double) fieldsNameValuePairs.getOrDefault(SCORE, this.score()), (java.lang.Boolean) fieldsNameValuePairs.getOrDefault(PASSED, this.passed()));
}

@java.lang.SuppressWarnings("unchecked")
public <T> ImmutableExamReport with(String fieldName, T fieldValue) {
    return new ImmutableExamReport(fieldName.equals(CANDIDATE_ID) ? (int) fieldValue : this.candidateId(), fieldName.equals(FULL_NAME) ? (java.lang.String) fieldValue : this.fullName(), fieldName.equals(CONTACT_INFO) ? (com.bayor.froporec.annotation.client.factorymthdsdemo.factorymthds.ContactInfo) fieldValue : this.contactInfo(), fieldName.equals(EXAM_ID) ? (java.lang.Integer) fieldValue : this.examId(), fieldName.equals(SUBMITTED_EXAM_CONTENT) ? (java.lang.String) fieldValue : this.submittedExamContent(), fieldName.equals(EXAM_DATE) ? (java.time.LocalDate) fieldValue : this.examDate(), fieldName.equals(SCORE) ? (java.lang.Double) fieldValue : this.score(), fieldName.equals(PASSED) ? (java.lang.Boolean) fieldValue : this.passed());
}




Sample POJO and Record classes for testing

https://github.com/mohamed-ashraf-bayor/froporec-annotation-client

Issues, Bugs, Suggestions

Contribute to the project’s growth by reporting issues or making improvement suggestions here



© 2021-2023, Froporec is an open source project, currently distributed under the MIT License