From bc3ed2671687b829fbdac4d1331aee1bf3811ba0 Mon Sep 17 00:00:00 2001
From: Chirag Dewan <chirag.dewan@ericsson.com>
Date: Tue, 16 Jul 2019 17:20:12 +0530
Subject: [PATCH] BAEL-2990 Automatic generation of the Builder pattern with
 FreeBuilder

---
 libraries-2/pom.xml                           |  11 +-
 .../com/baeldung/freebuilder/Employee.java    |  65 +++++++
 .../freebuilder/builder/classic/Address.java  |  25 +++
 .../freebuilder/builder/classic/Employee.java | 181 ++++++++++++++++++
 .../freebuilder/EmployeeBuilderUnitTest.java  | 150 +++++++++++++++
 .../classic/EmployeeBuilderUnitTest.java      |  32 ++++
 pom.xml                                       |   2 +-
 7 files changed, 463 insertions(+), 3 deletions(-)
 create mode 100644 libraries-2/src/main/java/com/baeldung/freebuilder/Employee.java
 create mode 100644 libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Address.java
 create mode 100644 libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Employee.java
 create mode 100644 libraries-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java
 create mode 100644 libraries-2/src/test/java/com/baeldung/freebuilder/builder/classic/EmployeeBuilderUnitTest.java

diff --git a/libraries-2/pom.xml b/libraries-2/pom.xml
index ff73888b69..c6be8df38d 100644
--- a/libraries-2/pom.xml
+++ b/libraries-2/pom.xml
@@ -142,9 +142,15 @@
             <groupId>org.apache.mesos</groupId>
             <artifactId>mesos</artifactId>
             <version>${mesos.library.version}</version>
+        </dependency>
+		<dependency>
+            <groupId>org.inferred</groupId>
+            <artifactId>freebuilder</artifactId>
+            <version>${freebuilder.version}</version>
+            <optional>true</optional>
         </dependency>
     </dependencies>
-
+		
     <properties>
 	    <mapdb.version>3.0.7</mapdb.version>
         <assertj.version>3.6.2</assertj.version>
@@ -165,5 +171,6 @@
         <gson.version>2.8.5</gson.version>
         <mockwebserver.version>3.14.2</mockwebserver.version>
         <handlebars.version>4.1.2</handlebars.version>
-    </properties>
+		<freebuilder.version>2.4.1</freebuilder.version>
+	</properties>
 </project>
diff --git a/libraries-2/src/main/java/com/baeldung/freebuilder/Employee.java b/libraries-2/src/main/java/com/baeldung/freebuilder/Employee.java
new file mode 100644
index 0000000000..08470cc5cd
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/freebuilder/Employee.java
@@ -0,0 +1,65 @@
+package com.baeldung.freebuilder;
+
+import com.baeldung.freebuilder.builder.classic.Address;
+import org.inferred.freebuilder.FreeBuilder;
+
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.UnaryOperator;
+
+@FreeBuilder
+public interface Employee {
+
+    String getName();
+
+    int getAge();
+
+    String getDepartment();
+
+    String getRole();
+
+    String getSupervisorName();
+
+    String getDesignation();
+
+    String getEmail();
+
+    long getPhoneNumber();
+
+    Optional<Boolean> getPermanent();
+
+    Optional<String> getDateOfJoining();
+
+    @Nullable
+    String getCurrentProject();
+
+    Address getAddress();
+
+    List<Long> getAccessTokens();
+
+    Map<String, Long> getAssetsSerialIdMapping();
+
+
+    class Builder extends Employee_Builder {
+
+        public Builder() {
+            // setting default value for department
+            setDepartment("Builder Pattern");
+        }
+
+        @Override
+        public Builder setEmail(String email) {
+            if (checkValidEmail(email))
+                return super.setEmail(email);
+            else
+                throw new IllegalArgumentException("Invalid email");
+
+        }
+
+        private boolean checkValidEmail(String email) {
+            return email.contains("@");
+        }
+    }
+}
diff --git a/libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Address.java b/libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Address.java
new file mode 100644
index 0000000000..8e53a9f155
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Address.java
@@ -0,0 +1,25 @@
+package com.baeldung.freebuilder.builder.classic;
+
+import org.inferred.freebuilder.FreeBuilder;
+
+import java.util.Optional;
+
+@FreeBuilder
+public interface Address {
+
+    Optional<String> getAddressLine1();
+
+    Optional<String> getAddressLine2();
+
+    Optional<String> getAddressLine3();
+
+    String getCity();
+
+    Optional<String> getState();
+
+    Optional<Long> getPinCode();
+
+    class Builder extends Address_Builder {
+
+    }
+}
\ No newline at end of file
diff --git a/libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Employee.java b/libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Employee.java
new file mode 100644
index 0000000000..bd9c8b0a17
--- /dev/null
+++ b/libraries-2/src/main/java/com/baeldung/freebuilder/builder/classic/Employee.java
@@ -0,0 +1,181 @@
+package com.baeldung.freebuilder.builder.classic;
+
+public class Employee {
+
+    private String name;
+    private int age;
+    private String department;
+    private String role;
+    private String supervisorName;
+    private String designation;
+    private String email;
+    private long phoneNumber;
+    private boolean isPermanent;
+    private Address address;
+
+    private Employee() {
+
+    }
+
+    private void setName(String name) {
+        this.name = name;
+    }
+
+    private void setAge(int age) {
+        this.age = age;
+    }
+
+    private void setDepartment(String department) {
+        this.department = department;
+    }
+
+    private void setRole(String role) {
+        this.role = role;
+    }
+
+    private void setSupervisorName(String supervisorName) {
+        this.supervisorName = supervisorName;
+    }
+
+    private void setDesignation(String designation) {
+        this.designation = designation;
+    }
+
+    private void setEmail(String email) {
+        this.email = email;
+    }
+
+    private void setPhoneNumber(long phoneNumber) {
+        this.phoneNumber = phoneNumber;
+    }
+
+    private void setPermanent(boolean permanent) {
+        isPermanent = permanent;
+    }
+
+    private void setAddress(Address address) {
+        this.address = address;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public int getAge() {
+        return age;
+    }
+
+    public String getDepartment() {
+        return department;
+    }
+
+    public String getRole() {
+        return role;
+    }
+
+    public String getSupervisorName() {
+        return supervisorName;
+    }
+
+    public String getDesignation() {
+        return designation;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public long getPhoneNumber() {
+        return phoneNumber;
+    }
+
+    public boolean isPermanent() {
+        return isPermanent;
+    }
+
+    public Address getAddress() {
+        return address;
+    }
+
+    public static class Builder {
+
+        private String name;
+        private int age;
+        private String department;
+        private String role;
+        private String supervisorName;
+        private String designation;
+        private String email;
+        private long phoneNumber;
+        private boolean isPermanent;
+        private Address address;
+
+        public Builder setName(String name) {
+            this.name = name;
+            return this;
+        }
+
+        public Builder setAge(int age) {
+            this.age = age;
+            return this;
+        }
+
+        public Builder setDepartment(String department) {
+            this.department = department;
+            return this;
+        }
+
+        public Builder setRole(String role) {
+            this.role = role;
+            return this;
+        }
+
+        public Builder setSupervisorName(String supervisorName) {
+            this.supervisorName = supervisorName;
+            return this;
+        }
+
+        public Builder setDesignation(String designation) {
+            this.designation = designation;
+            return this;
+        }
+
+        public Builder setEmail(String email) {
+            this.email = email;
+            return this;
+        }
+
+        public Builder setPhoneNumber(long phoneNumber) {
+            this.phoneNumber = phoneNumber;
+            return this;
+        }
+
+        public Builder setPermanent(boolean permanent) {
+            isPermanent = permanent;
+            return this;
+        }
+
+        public Builder setAddress(Address address) {
+            this.address = address;
+            return this;
+        }
+
+        public Employee build() {
+            Employee employee = new Employee();
+            employee.setName(this.name);
+            employee.setAge(this.age);
+            employee.setDepartment(this.department);
+            employee.setAddress(this.address);
+            employee.setDesignation(this.designation);
+            employee.setEmail(this.email);
+            employee.setPermanent(this.isPermanent);
+            employee.setName(this.name);
+            employee.setSupervisorName(this.supervisorName);
+            employee.setPhoneNumber(this.phoneNumber);
+
+            return employee;
+
+        }
+    }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java b/libraries-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java
new file mode 100644
index 0000000000..e3a9e49633
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/freebuilder/EmployeeBuilderUnitTest.java
@@ -0,0 +1,150 @@
+package com.baeldung.freebuilder;
+
+import com.baeldung.freebuilder.builder.classic.Address;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class EmployeeBuilderUnitTest {
+
+    private static final int PIN_CODE = 223344;
+    public static final String CITY_NAME = "New York";
+
+    @Test
+    public void whenBuildEmployeeWithAddress_thenReturnEmployeeWithValidAddress() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setPermanent(true).setRole("developer").setAddress(address).build();
+
+        // then
+        assertTrue(employee.getAddress().getCity().equalsIgnoreCase(CITY_NAME));
+
+    }
+
+    @Test
+    public void whenMapPincodeInAddress_thenReturnEmployeeWithValidAddressPincode() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).setPinCode(PIN_CODE).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setPermanent(true).setRole("developer").setAddress(address).build();
+
+        // then
+        assertTrue(employee.getAddress().getPinCode().get() == PIN_CODE);
+
+    }
+
+    @Test
+    public void whenOptionalFields_thenReturnEmployeeWithEmptyValues() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setNullablePermanent(null).setDateOfJoining(Optional.empty()).setRole("developer")
+                .setAddress(address).build();
+
+        // then
+        assertFalse(employee.getDateOfJoining().isPresent());
+
+    }
+
+    @Test
+    public void whenNullableFields_thenReturnEmployeeWithNullValueForField() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setNullablePermanent(null).setDateOfJoining(Optional.empty()).setRole("developer")
+                .setAddress(address).build();
+
+        // then
+        assertNull(employee.getCurrentProject());
+
+    }
+
+    @Test
+    public void whenCollectionFields_thenReturnEmployeeWithValues() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setNullablePermanent(null).setDateOfJoining(Optional.empty()).setRole("developer")
+                .addAccessTokens(1221819L).addAccessTokens(1223441L, 134567L).setAddress(address).build();
+
+        // then
+        assertTrue(employee.getAccessTokens().size() == 3);
+
+    }
+
+    @Test
+    public void whenMapFields_thenReturnEmployeeWithValues() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setNullablePermanent(null).setDateOfJoining(Optional.empty()).setRole("developer")
+                .addAccessTokens(1221819L).addAccessTokens(1223441L, 134567L).putAssetsSerialIdMapping("Laptop", 12345L).setAddress(address).build();
+
+        // then
+        assertTrue(employee.getAssetsSerialIdMapping().size() == 1);
+
+    }
+
+    @Test
+    public void whenNestedBuilderTypes_thenReturnEmployeeWithValues() {
+
+        // when
+        Address.Builder addressBuilder = new Address.Builder();
+        Address address = addressBuilder.setCity(CITY_NAME).build();
+
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung").setAge(10).setDesignation("author").setEmail("abc@xyz.com").setSupervisorName("Admin").setPhoneNumber(4445566).setNullablePermanent(null).setDateOfJoining(Optional.empty()).setRole("developer")
+                .addAccessTokens(1221819L).addAccessTokens(1223441L, 134567L).putAssetsSerialIdMapping("Laptop", 12345L).setAddress(address).mutateAddress(a -> a.setPinCode(112200)).build();
+
+        // then
+        assertTrue(employee.getAssetsSerialIdMapping().size() == 1);
+
+    }
+
+    @Test()
+    public void whenPartialEmployeeWithValidEmail_thenReturnEmployeeWithEmail() {
+
+        // when
+        Employee.Builder builder = new Employee.Builder();
+
+        Employee employee = builder.setName("baeldung")
+          .setAge(10)
+          .setEmail("abc@xyz.com")
+          .buildPartial();
+
+        assertNotNull(employee.getEmail());
+    }
+
+}
diff --git a/libraries-2/src/test/java/com/baeldung/freebuilder/builder/classic/EmployeeBuilderUnitTest.java b/libraries-2/src/test/java/com/baeldung/freebuilder/builder/classic/EmployeeBuilderUnitTest.java
new file mode 100644
index 0000000000..930820009c
--- /dev/null
+++ b/libraries-2/src/test/java/com/baeldung/freebuilder/builder/classic/EmployeeBuilderUnitTest.java
@@ -0,0 +1,32 @@
+package com.baeldung.freebuilder.builder.classic;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+
+class EmployeeBuilderUnitTest {
+
+    private static final String CITY = "New York";
+
+    @Test
+    public void whenBuildEmployeeWithAddress_thenReturnEmployeeWithValidAddress() {
+
+        // when
+        Employee.Builder emplBuilder = new Employee.Builder();
+
+        Employee employee = emplBuilder
+          .setName("baeldung")
+          .setAge(12)
+          .setDepartment("Builder Pattern")
+          .setDesignation("Author")
+          .setEmail("abc@xyz.com")
+          .setPermanent(true)
+          .setSupervisorName("Admin")
+          .setPhoneNumber(4445566)
+          .build();
+
+        //then
+        Assertions.assertTrue(employee.getAddress().getCity().equalsIgnoreCase(CITY));
+    }
+
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 05c719ec7c..90cea73a9d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1571,7 +1571,7 @@
 		<directory-maven-plugin.version>0.3.1</directory-maven-plugin.version>
 		<maven-install-plugin.version>2.5.1</maven-install-plugin.version>
 		<custom-pmd.version>0.0.1</custom-pmd.version>
-		<gitflow-incremental-builder.version>3.8</gitflow-incremental-builder.version>
+		<gitflow-incremental-builder.version>3.7</gitflow-incremental-builder.version>
 		<maven-jxr-plugin.version>2.3</maven-jxr-plugin.version>
 		<!-- <maven-pmd-plugin.version>3.9.0</maven-pmd-plugin.version> -->
 		<maven-pmd-plugin.version>3.8</maven-pmd-plugin.version>