
// ClosureTester.java
// https://www.demo2s.com/java/apache-commons-forclosure-tutorial-with-examples.html
// Andrew Davison, ad@coe.psu.ac.th, May 2023

/* A Closure is a block of code which can be executed 
   inside some other block, function or iteration. 

   Implementations of common closures are provided 
   by ClosureUtils.  

     Similar to Java's Consumer functional interface
     https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/package-summary.html

  Usage:
    > compile ClosureTester.java
    > run ClosureTester
*/


import java.util.*;

import org.apache.commons.collections4.*;
import org.apache.commons.collections4.functors.*;



public class ClosureTester
{
  private static List<Employee> employees;


  public static void main(String[] args)
  {
    employees = new ArrayList<>( Arrays.asList(
                       new Employee("Justin", "male", 20000),
                       new Employee("Wang", "male", 22000),
                       new Employee("Kelly", "female", 24000) ));
    // System.out.println(employees);
    // IterableUtils.forEach(employees, System.out::println);
    employees.forEach(System.out::println);

    employees.forEach( e -> e.setSalary(e.getSalary() * 1.2));
    System.out.println("Pay increase: " + employees);

    ifClosure();
    whileClosure();
    chainClosure();
    forClosure();
    switchClosure();
  } /* main */



  private static void ifClosure()
  /* acts as an if statement calling one or other 
     closure based on a test predicate */
  {
    System.out.println("\n--------- ifClosure");

    IfClosure<Employee> ifC = 
          new IfClosure<>(e -> e.getSalary() < 22000, 
                          e -> e.setSalary(e.getSalary() * 1.2),
                          e -> e.setSalary(e.getSalary() + 1)  );

    IterableUtils.forEach(employees, ifC);
    System.out.println("Pay ifC: " + employees);
  } /* ifClosure */


  private static void whileClosure()
  // executes a closure repeatedly until a condition is met
  {
    System.out.println("\n---------  whileClosure");

    Predicate<Employee> condPred =  
                       e->e.getSalary() < 22000;

    Closure<Employee> cl =
           e->e.setSalary(e.getSalary() * 1.01);

    WhileClosure<Employee> wc_WhileDo = 
            new WhileClosure<>(condPred, cl, false);

    WhileClosure<Employee> wc_DoWhile = 
            new WhileClosure<>(condPred, cl, true);


    IterableUtils.forEach(employees, wc_DoWhile);
    System.out.println("DoWhile: " + employees);

    IterableUtils.forEach(employees, wc_WhileDo);
    System.out.println("WhileDo: " + employees);
  } /* whileClosure */


  private static void chainClosure()
  //  chain the specified closures together
  {
    System.out.println("\n---------  chainClosure");

    Closure<Employee> c1 =
      e->e.setSalary(e.getSalary() * 1.2);

    Closure<Employee> c2 = 
       e->e.setName(e.getName() + " can do better!");

    Closure<Employee> c3 = 
        e->e.setSalary(e.getSalary() + 1);


    Closure<Employee> chain = ClosureUtils.chainedClosure(
                             Arrays.asList(c1, c2, c3));

    IterableUtils.forEach(employees, chain);
    System.out.println("c1, c2, c3: " + employees);
  } /* chainClosure */


  private static void forClosure()
  // call another closure n times
  {
    System.out.println("\n---------  forClosure");

    ForClosure<Employee> forClosure = new ForClosure<>(100, 
                         e->e.setSalary(e.getSalary() + 1));  // 100 times

    IterableUtils.forEach(employees, forClosure);
    System.out.println("for 100: " + employees);
  } /* forClosure */



  private static void switchClosure()
  /* calls the closure whose predicate returns true, 
     like a switch
  */
  {
    System.out.println("\n---------  switchClosure");

    Map<Predicate<Employee>, Closure<Employee>> swMap =
                                         new HashMap<>();
    swMap.put(e->e.getSalary() < 22000, 
              e->e.setSalary(e.getSalary() * 1.2) );

    swMap.put(e->e.getName().length() > 1, 
              e->e.setSalary(e.getSalary() + 500) );

    swMap.put(e->e.getName().equals("Keli"), 
              e->e.setSalary(e.getSalary() + 50000) );

    IterableUtils.forEach(employees, ClosureUtils.switchClosure(swMap));
    System.out.println(employees);
  } /* switchClosure */


}  // end of ClosureTester class

