// Path: flow-bookstore/bookstore-ui/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/SerializableUtils.java
// public interface SerializableUtils<T> extends Comparator<T>, Serializable {
// 
//     static <T, U> Function<T, U> function(SerializableFunction<T, U> fn) {
//         return fn;
//     }
// 
//     static <T> ToIntFunction<T> toIntFunction(SerializableToIntFunction<T> fn) {
//         return fn;
//     }
// 
//     interface SerializableToIntFunction<R> extends ToIntFunction<R>, Serializable {}
// 
//     interface FileReceiver extends Receiver {
//         FileInfo getFileInfo();
// 
//         default String getFileName() {
//             return Optional.ofNullable(getFileInfo()).map(FileInfo::getFileName).orElse("");
//         }
// 
//         default InputStream getInputStream() {
//             FileInfo file = getFileInfo();
//             if (file != null) {
//                 try {
//                     return new FileInputStream(file.file);
//                 } catch (IOException e) {
//                     Logger.getLogger(getClass().getName()).log(Level.WARNING,
//                         "Failed to create InputStream for: '" + getFileName()
//                             + "'", e);
//                 }
//             }
//             return new ByteArrayInputStream(new byte[0]);
//         }
// 
//         class FileInfo implements Serializable {
//             private final File file;
//             private final String fileName;
//             private final String mimeType;
// 
//             public FileInfo(File file, String fileName, String mimeType) {
//                 this.file = file;
//                 this.fileName = fileName;
//                 this.mimeType = mimeType;
//             }
// 
//             public String getFileName() {
//                 return fileName;
//             }
// 
//             OutputStream getOutputBuffer() {
//                 try {
//                     return new FileOutputStream(file);
//                 } catch (FileNotFoundException e) {
//                     throw new RuntimeException(e);
//                 }
//             }
//         }
//     }
// 
//     static FileReceiver newFileBuffer() {
//         return new SerializableFileBuffer();
//     }
// }
// 
// Path: flow-bookstore/bookstore-backend/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/backend/data/Category.java
// public class Category implements Serializable {
// 
//     @NotNull
//     private int id;
//     @NotNull
//     private String name;
// 
//     public int getId() {
//         return id;
//     }
// 
//     public void setId(int id) {
//         this.id = id;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public void setName(String name) {
//         this.name = name;
//     }
// 
//     @Override
//     public String toString() {
//         return getName();
//     }
// }
// 
// Path: flow-bookstore/bookstore-backend/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/backend/data/Product.java
// public class Product implements Serializable {
// 
//     @NotNull
//     private int id = -1;
//     @NotNull
//     @Size(min = 2, message = "Product name must have at least two characters")
//     private String productName = "";
//     @Min(0)
//     private BigDecimal price = BigDecimal.ZERO;
//     private Set<Category> category;
//     @Min(value = 0, message = "Can't have negative amount in stock")
//     private int stockCount = 0;
//     @NotNull
//     private Availability availability = Availability.COMING;
// 
//     private String image;
// 
//     public int getId() {
//         return id;
//     }
// 
//     public void setId(int id) {
//         this.id = id;
//     }
// 
//     public String getProductName() {
//         return productName;
//     }
// 
//     public void setProductName(String productName) {
//         this.productName = productName;
//     }
// 
//     public BigDecimal getPrice() {
//         return price;
//     }
// 
//     public void setPrice(BigDecimal price) {
//         this.price = price;
//     }
// 
//     public Set<Category> getCategory() {
//         return category;
//     }
// 
//     public void setCategory(Set<Category> category) {
//         this.category = category;
//     }
// 
//     public int getStockCount() {
//         return stockCount;
//     }
// 
//     public void setStockCount(int stockCount) {
//         this.stockCount = stockCount;
//     }
// 
//     public Availability getAvailability() {
//         return availability;
//     }
// 
//     public void setAvailability(Availability availability) {
//         this.availability = availability;
//     }
// 
//     public boolean isNewProduct() {
//         return getId() == -1;
//     }
// 
//     public String getImage() {
//         return image;
//     }
// 
//     public void setImage(String image) {
//         this.image = image;
//     }
// }


import com.github.mcollovati.vertxvaadin.flowdemo.SerializableUtils;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.data.renderer.TemplateRenderer;
import com.github.mcollovati.vertxvaadin.flowdemo.backend.data.Category;
import com.github.mcollovati.vertxvaadin.flowdemo.backend.data.Product;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.stream.Collectors;
package com.github.mcollovati.vertxvaadin.flowdemo.crud;



/**
 * Grid of products, handling the visual presentation and filtering of a set of
 * items. This version uses an in-memory data source that is suitable for small
 * data sets.
 */
public class ProductGrid extends Grid<Product> {

    public ProductGrid() {
        setSizeFull();

        addColumn(Product::getProductName)
                .setHeader("Product name")
                .setFlexGrow(20)
                .setSortable(true);

        // Format and add " €" to price
        final DecimalFormat decimalFormat = new DecimalFormat();
        decimalFormat.setMaximumFractionDigits(2);
        decimalFormat.setMinimumFractionDigits(2);

        // To change the text alignment of the column, a template is used.
        final String priceTemplate = "<div style='text-align: right'>[[item.price]]</div>";
        addColumn(TemplateRenderer.<Product>of(priceTemplate)
                .withProperty("price", product -> decimalFormat.format(product.getPrice()) + " €"))
                .setHeader("Price")
// Path: flow-bookstore/bookstore-ui/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/SerializableUtils.java
// public interface SerializableUtils<T> extends Comparator<T>, Serializable {
// 
//     static <T, U> Function<T, U> function(SerializableFunction<T, U> fn) {
//         return fn;
//     }
// 
//     static <T> ToIntFunction<T> toIntFunction(SerializableToIntFunction<T> fn) {
//         return fn;
//     }
// 
//     interface SerializableToIntFunction<R> extends ToIntFunction<R>, Serializable {}
// 
//     interface FileReceiver extends Receiver {
//         FileInfo getFileInfo();
// 
//         default String getFileName() {
//             return Optional.ofNullable(getFileInfo()).map(FileInfo::getFileName).orElse("");
//         }
// 
//         default InputStream getInputStream() {
//             FileInfo file = getFileInfo();
//             if (file != null) {
//                 try {
//                     return new FileInputStream(file.file);
//                 } catch (IOException e) {
//                     Logger.getLogger(getClass().getName()).log(Level.WARNING,
//                         "Failed to create InputStream for: '" + getFileName()
//                             + "'", e);
//                 }
//             }
//             return new ByteArrayInputStream(new byte[0]);
//         }
// 
//         class FileInfo implements Serializable {
//             private final File file;
//             private final String fileName;
//             private final String mimeType;
// 
//             public FileInfo(File file, String fileName, String mimeType) {
//                 this.file = file;
//                 this.fileName = fileName;
//                 this.mimeType = mimeType;
//             }
// 
//             public String getFileName() {
//                 return fileName;
//             }
// 
//             OutputStream getOutputBuffer() {
//                 try {
//                     return new FileOutputStream(file);
//                 } catch (FileNotFoundException e) {
//                     throw new RuntimeException(e);
//                 }
//             }
//         }
//     }
// 
//     static FileReceiver newFileBuffer() {
//         return new SerializableFileBuffer();
//     }
// }
// 
// Path: flow-bookstore/bookstore-backend/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/backend/data/Category.java
// public class Category implements Serializable {
// 
//     @NotNull
//     private int id;
//     @NotNull
//     private String name;
// 
//     public int getId() {
//         return id;
//     }
// 
//     public void setId(int id) {
//         this.id = id;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public void setName(String name) {
//         this.name = name;
//     }
// 
//     @Override
//     public String toString() {
//         return getName();
//     }
// }
// 
// Path: flow-bookstore/bookstore-backend/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/backend/data/Product.java
// public class Product implements Serializable {
// 
//     @NotNull
//     private int id = -1;
//     @NotNull
//     @Size(min = 2, message = "Product name must have at least two characters")
//     private String productName = "";
//     @Min(0)
//     private BigDecimal price = BigDecimal.ZERO;
//     private Set<Category> category;
//     @Min(value = 0, message = "Can't have negative amount in stock")
//     private int stockCount = 0;
//     @NotNull
//     private Availability availability = Availability.COMING;
// 
//     private String image;
// 
//     public int getId() {
//         return id;
//     }
// 
//     public void setId(int id) {
//         this.id = id;
//     }
// 
//     public String getProductName() {
//         return productName;
//     }
// 
//     public void setProductName(String productName) {
//         this.productName = productName;
//     }
// 
//     public BigDecimal getPrice() {
//         return price;
//     }
// 
//     public void setPrice(BigDecimal price) {
//         this.price = price;
//     }
// 
//     public Set<Category> getCategory() {
//         return category;
//     }
// 
//     public void setCategory(Set<Category> category) {
//         this.category = category;
//     }
// 
//     public int getStockCount() {
//         return stockCount;
//     }
// 
//     public void setStockCount(int stockCount) {
//         this.stockCount = stockCount;
//     }
// 
//     public Availability getAvailability() {
//         return availability;
//     }
// 
//     public void setAvailability(Availability availability) {
//         this.availability = availability;
//     }
// 
//     public boolean isNewProduct() {
//         return getId() == -1;
//     }
// 
//     public String getImage() {
//         return image;
//     }
// 
//     public void setImage(String image) {
//         this.image = image;
//     }
// }


// Path: flow-bookstore/bookstore-ui/src/main/java/com/github/mcollovati/vertxvaadin/flowdemo/crud/ProductGrid.java
import com.github.mcollovati.vertxvaadin.flowdemo.SerializableUtils;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.data.renderer.TemplateRenderer;
import com.github.mcollovati.vertxvaadin.flowdemo.backend.data.Category;
import com.github.mcollovati.vertxvaadin.flowdemo.backend.data.Product;
import java.text.DecimalFormat;
import java.util.Comparator;
import java.util.stream.Collectors;

package com.github.mcollovati.vertxvaadin.flowdemo.crud;



/**
 * Grid of products, handling the visual presentation and filtering of a set of
 * items. This version uses an in-memory data source that is suitable for small
 * data sets.
 */
public class ProductGrid extends Grid<Product> {

    public ProductGrid() {
        setSizeFull();

        addColumn(Product::getProductName)
                .setHeader("Product name")
                .setFlexGrow(20)
                .setSortable(true);

        // Format and add " €" to price
        final DecimalFormat decimalFormat = new DecimalFormat();
        decimalFormat.setMaximumFractionDigits(2);
        decimalFormat.setMinimumFractionDigits(2);

        // To change the text alignment of the column, a template is used.
        final String priceTemplate = "<div style='text-align: right'>[[item.price]]</div>";
        addColumn(TemplateRenderer.<Product>of(priceTemplate)
                .withProperty("price", product -> decimalFormat.format(product.getPrice()) + " €"))
                .setHeader("Price")
                .setComparator(Comparator.comparing(SerializableUtils.function(Product::getPrice)))