// Path: domain-model/src/main/java/net/safedata/java8/training/model/Product.java
// public class Product extends AbstractEntity {
// 
//     private final int id;
//     private final String name;
//     private final double price;
//     private final Discount discount;
//     private final List<String> tags;
// 
//     public Product(final int id, final String name, final double price, final Discount discount,
//                    final List<String> tags) {
//         this.id = id;
//         this.name = name;
//         this.price = price;
//         this.discount = discount;
//         this.tags = tags;
//     }
// 
//     public Product(final int id, final String name, final double price, final Discount discount) {
//         this.id = id;
//         this.name = name;
//         this.price = price;
//         this.discount = discount;
//         this.tags = null;
//     }
// 
//     public Product(final int id, final String name, final double price) {
//         this.id = id;
//         this.name = name;
//         this.price = price;
//         this.discount = null;
//         this.tags = null;
//     }
// 
//     public int getId() {
//         return id;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public double getPrice() {
//         return price;
//     }
// 
//     public Optional<Discount> getDiscount() {
//         return Optional.ofNullable(discount);
//     }
// 
//     public Optional<List<String>> getTags() {
//         return Optional.ofNullable(tags);
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (!(o instanceof Product)) return false;
//         Product product = (Product) o;
//         return id == product.id &&
//                 Objects.equals(name, product.name) &&
//                 Objects.equals(price, product.price);
//     }
// 
//     @Override
//     public int hashCode() {
//         return Objects.hash(id, name, price);
//     }
// 
//     @Override
//     public String toString() {
//         return id + ", " + name + ": " + price;
//     }
// }
// 
// Path: s01/s01e04-functions/src/main/java/net/safedata/java8/training/function/service/ProductService.java
// public class ProductService {
// 
//     public Set<String> getSamsungTabletDescriptions() {
//         final Section tabletsSection = getTabletsSection();
// 
//         final List<Product> tablets = getProductsOrThrow(tabletsSection);
// 
//         return getSamsungTabletsDescriptions(tablets);
//     }
// 
//     private Section getTabletsSection() {
//         return StoreSetup.getDefaultStore()
//                          .getStoreSections()
//                          .stream()
//                          .filter(section -> section.getName().equals(StoreSection.Tablets))
//                          .findFirst()
//                          .orElseThrow(() -> new IllegalArgumentException("There's no section named 'Tablets'"));
//     }
// 
//     private Set<String> getSamsungTabletsDescriptions(final List<Product> tablets) {
//         return tablets.stream()
//                       .filter(samsungProductsFilter())
//                       .map(convertProductToString())
//                       .peek(traceItem())
//                       .collect(Collectors.toSet());
//     }
// 
//     private List<Product> getProductsOrThrow(Section tabletsSection) {
//         return tabletsSection.getProducts()
//                              .orElseThrow(() -> new IllegalArgumentException("There are no products"));
//     }
// 
//     private Consumer<String> traceItem() {
//         return System.out::println;
//     }
// 
//     private Predicate<Product> samsungProductsFilter() {
//         return product -> product.getName().contains("Samsung");
//     }
// 
//     private Function<Product, String> convertProductToString() {
//         return product -> "The product is: " + product.toString();
//     }
// }


import net.safedata.java8.training.model.Product;
import net.safedata.java8.training.function.service.ProductService;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;

        System.out.println(first.andThen(second)
                                .andThen(third)
                                .apply("something"));

        System.out.println();

        // first applies the {@code before} function to its input,
        // and then applies this function to the result
        System.out.println(first.compose(second)
                                .compose(third)
                                .apply("f(g(h(x)))")); // any text can go in here
    }

    private static void mapFunctions() {
        final Map<Integer, String> weekDays = new HashMap<>();
        weekDays.put(1, "Monday");
        weekDays.put(2, "Tuesday");

        // new methods were added to the Map interface
        weekDays.computeIfAbsent(3, value -> "The 3rd day"); // aka Wednesday

        weekDays.forEach((key, value) -> System.out.println(key + " -> " + value));
    }

    private static void productFunctions() {
        final Product aFancyAppleProduct = new Product(10, "iSomething", 500);
        final Function<Product, String> productPrinter = it -> it.toString(); //Product::toString
        productPrinter.apply(aFancyAppleProduct);

// Path: domain-model/src/main/java/net/safedata/java8/training/model/Product.java
// public class Product extends AbstractEntity {
// 
//     private final int id;
//     private final String name;
//     private final double price;
//     private final Discount discount;
//     private final List<String> tags;
// 
//     public Product(final int id, final String name, final double price, final Discount discount,
//                    final List<String> tags) {
//         this.id = id;
//         this.name = name;
//         this.price = price;
//         this.discount = discount;
//         this.tags = tags;
//     }
// 
//     public Product(final int id, final String name, final double price, final Discount discount) {
//         this.id = id;
//         this.name = name;
//         this.price = price;
//         this.discount = discount;
//         this.tags = null;
//     }
// 
//     public Product(final int id, final String name, final double price) {
//         this.id = id;
//         this.name = name;
//         this.price = price;
//         this.discount = null;
//         this.tags = null;
//     }
// 
//     public int getId() {
//         return id;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public double getPrice() {
//         return price;
//     }
// 
//     public Optional<Discount> getDiscount() {
//         return Optional.ofNullable(discount);
//     }
// 
//     public Optional<List<String>> getTags() {
//         return Optional.ofNullable(tags);
//     }
// 
//     @Override
//     public boolean equals(Object o) {
//         if (this == o) return true;
//         if (!(o instanceof Product)) return false;
//         Product product = (Product) o;
//         return id == product.id &&
//                 Objects.equals(name, product.name) &&
//                 Objects.equals(price, product.price);
//     }
// 
//     @Override
//     public int hashCode() {
//         return Objects.hash(id, name, price);
//     }
// 
//     @Override
//     public String toString() {
//         return id + ", " + name + ": " + price;
//     }
// }
// 
// Path: s01/s01e04-functions/src/main/java/net/safedata/java8/training/function/service/ProductService.java
// public class ProductService {
// 
//     public Set<String> getSamsungTabletDescriptions() {
//         final Section tabletsSection = getTabletsSection();
// 
//         final List<Product> tablets = getProductsOrThrow(tabletsSection);
// 
//         return getSamsungTabletsDescriptions(tablets);
//     }
// 
//     private Section getTabletsSection() {
//         return StoreSetup.getDefaultStore()
//                          .getStoreSections()
//                          .stream()
//                          .filter(section -> section.getName().equals(StoreSection.Tablets))
//                          .findFirst()
//                          .orElseThrow(() -> new IllegalArgumentException("There's no section named 'Tablets'"));
//     }
// 
//     private Set<String> getSamsungTabletsDescriptions(final List<Product> tablets) {
//         return tablets.stream()
//                       .filter(samsungProductsFilter())
//                       .map(convertProductToString())
//                       .peek(traceItem())
//                       .collect(Collectors.toSet());
//     }
// 
//     private List<Product> getProductsOrThrow(Section tabletsSection) {
//         return tabletsSection.getProducts()
//                              .orElseThrow(() -> new IllegalArgumentException("There are no products"));
//     }
// 
//     private Consumer<String> traceItem() {
//         return System.out::println;
//     }
// 
//     private Predicate<Product> samsungProductsFilter() {
//         return product -> product.getName().contains("Samsung");
//     }
// 
//     private Function<Product, String> convertProductToString() {
//         return product -> "The product is: " + product.toString();
//     }
// }


// Path: s01/s01e04-functions/src/main/java/net/safedata/java8/training/function/FunctionsMain.java
import net.safedata.java8.training.model.Product;
import net.safedata.java8.training.function.service.ProductService;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;


        System.out.println(first.andThen(second)
                                .andThen(third)
                                .apply("something"));

        System.out.println();

        // first applies the {@code before} function to its input,
        // and then applies this function to the result
        System.out.println(first.compose(second)
                                .compose(third)
                                .apply("f(g(h(x)))")); // any text can go in here
    }

    private static void mapFunctions() {
        final Map<Integer, String> weekDays = new HashMap<>();
        weekDays.put(1, "Monday");
        weekDays.put(2, "Tuesday");

        // new methods were added to the Map interface
        weekDays.computeIfAbsent(3, value -> "The 3rd day"); // aka Wednesday

        weekDays.forEach((key, value) -> System.out.println(key + " -> " + value));
    }

    private static void productFunctions() {
        final Product aFancyAppleProduct = new Product(10, "iSomething", 500);
        final Function<Product, String> productPrinter = it -> it.toString(); //Product::toString
        productPrinter.apply(aFancyAppleProduct);

        final ProductService productService = new ProductService();