Subject: [PATCH] 6_jaxb_filter
---
Index: src/test/java/ru/javaops/docjava/TestData.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/ru/javaops/docjava/TestData.java b/src/test/java/ru/javaops/docjava/TestData.java
--- a/src/test/java/ru/javaops/docjava/TestData.java	(revision 42b9e1ab07bf421841ee5e99a7249ba165232968)
+++ b/src/test/java/ru/javaops/docjava/TestData.java	(date 1695983014739)
@@ -4,39 +4,58 @@
 import ru.javaops.docjava.schema.Meals;
 import ru.javaops.docjava.schema.RoleTypes;
 import ru.javaops.docjava.schema.User;
+import ru.javaops.docjava.util.Util;
 
 import java.io.File;
 import java.time.Month;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 import static java.time.LocalDateTime.of;
 
 public class TestData {
     public static final File inputFile = new File("in/usersWithMeals.xml");
 
+    public static final Map<String, Object> paramsMap =
+            Util.parseParams(List.of("startDate=2023-01-30", "endDate=2023-01-30", "startTime=11:00", "endTime=19:00"));
+
     public static final Date registered = new Date(123, Calendar.JANUARY, 1, 13, 0, 0);
 
+    public static final Meal userLunch = new Meal("Обед", 1000, of(2023, Month.JANUARY, 30, 13, 0, 0), false);
     public static final Meals userMeals = new Meals(List.of(
-            new Meal("Завтрак", 500, of(2023, Month.JANUARY, 30, 10, 0, 0), null),
-            new Meal("Обед", 1000, of(2023, Month.JANUARY, 30, 13, 0, 0), null),
-            new Meal("Ужин", 500, of(2023, Month.JANUARY, 30, 20, 0, 0), null),
-            new Meal("Еда на граничное значение", 100, of(2023, Month.JANUARY, 31, 0, 0, 0), null),
-            new Meal("Завтрак", 500, of(2023, Month.JANUARY, 31, 10, 0, 0), null),
-            new Meal("Обед", 1000, of(2023, Month.JANUARY, 31, 13, 0, 0), null),
-            new Meal("Ужин", 510, of(2023, Month.JANUARY, 31, 20, 0, 0), null)
+            new Meal("Завтрак", 500, of(2023, Month.JANUARY, 30, 10, 0, 0), false),
+            userLunch,
+            new Meal("Ужин", 500, of(2023, Month.JANUARY, 30, 20, 0, 0), false),
+            new Meal("Еда на граничное значение", 100, of(2023, Month.JANUARY, 31, 0, 0, 0), true),
+            new Meal("Завтрак", 500, of(2023, Month.JANUARY, 31, 10, 0, 0), true),
+            new Meal("Обед", 1000, of(2023, Month.JANUARY, 31, 13, 0, 0), true),
+            new Meal("Ужин", 510, of(2023, Month.JANUARY, 31, 20, 0, 0), true)
     ));
     public static final User user = new User("User", "user@yandex.ru", "password", 2005, registered, true,
-            List.of(RoleTypes.USER), userMeals);
+            List.of(RoleTypes.USER), null);
 
+    public static final Meal adminLunch = new Meal("Админ ланч", 510, of(2023, Month.JANUARY, 30, 14, 0, 0), false);
     public static final Meals adminMeals = new Meals(List.of(
-            new Meal("Админ ланч", 510, of(2023, Month.JANUARY, 30, 14, 0, 0), null),
-            new Meal("Админ ужин", 730, of(2023, Month.JANUARY, 30, 21, 0, 0), null)
+            adminLunch,
+            new Meal("Админ ужин", 730, of(2023, Month.JANUARY, 30, 21, 0, 0), false)
     ));
     public static final User admin = new User("Admin", "admin@gmail.com", "admin", 1900, registered, true,
-            List.of(RoleTypes.ADMIN, RoleTypes.USER), adminMeals);
+            List.of(RoleTypes.ADMIN, RoleTypes.USER), null);
 
     public static final User guest = new User("Guest", "guest@gmail.com", "guest", 2000, registered, false,
             List.of(), null);
+
+    public static List<User> getUsers() {
+        user.setMeals(userMeals);
+        admin.setMeals(adminMeals);
+        return List.of(user, admin, guest);
+    }
+
+    public static List<User> getFilteredUsers() {
+        user.setMeals(new Meals(List.of(userLunch)));
+        admin.setMeals(new Meals(List.of(adminLunch)));
+        return List.of(user, admin, guest);
+    }
 }
\ No newline at end of file
Index: src/test/java/ru/javaops/docjava/xml/jaxb/JaxbUtilTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/ru/javaops/docjava/xml/jaxb/JaxbUtilTest.java b/src/test/java/ru/javaops/docjava/xml/jaxb/JaxbUtilTest.java
--- a/src/test/java/ru/javaops/docjava/xml/jaxb/JaxbUtilTest.java	(revision 42b9e1ab07bf421841ee5e99a7249ba165232968)
+++ b/src/test/java/ru/javaops/docjava/xml/jaxb/JaxbUtilTest.java	(date 1695983014733)
@@ -7,7 +7,7 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.List;
+import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -16,13 +16,19 @@
 public class JaxbUtilTest {
 
     @Test
-    public void unmarshalOK() throws JAXBException, IOException {
-        UsersWithMeals usersWithMeals = JaxbUtil.unmarshal(inputFile);
-        assertEquals(new UsersWithMeals.Users(List.of(user, admin, guest)), usersWithMeals.getUsers());
+    public void processOK() throws JAXBException, IOException {
+        UsersWithMeals usersWithMeals = JaxbUtil.process(inputFile, Map.of());
+        assertEquals(new UsersWithMeals.Users(getUsers()), usersWithMeals.getUsers());
     }
 
     @Test
-    void unmarshalNOK() {
+    void processFiltered() throws IOException, JAXBException {
+        UsersWithMeals usersWithMeals = JaxbUtil.process(inputFile, paramsMap);
+        assertEquals(new UsersWithMeals.Users(getFilteredUsers()), usersWithMeals.getUsers());
+    }
+
+    @Test
+    void processNOK() {
         assertThrows(UnmarshalException.class,
                 () -> JaxbUtil.unmarshal(new File("in/badXmlFile.xml")), "Except bad format exception");
     }
Index: src/main/java/ru/javaops/docjava/xml/jaxb/JaxbUtil.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/ru/javaops/docjava/xml/jaxb/JaxbUtil.java b/src/main/java/ru/javaops/docjava/xml/jaxb/JaxbUtil.java
--- a/src/main/java/ru/javaops/docjava/xml/jaxb/JaxbUtil.java	(revision 42b9e1ab07bf421841ee5e99a7249ba165232968)
+++ b/src/main/java/ru/javaops/docjava/xml/jaxb/JaxbUtil.java	(date 1695983138502)
@@ -5,12 +5,14 @@
 import lombok.experimental.UtilityClass;
 import ru.javaops.docjava.schema.ObjectFactory;
 import ru.javaops.docjava.schema.UsersWithMeals;
+import ru.javaops.docjava.util.MealsUtil;
 import ru.javaops.docjava.xml.xsd.Schemas;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.Reader;
 import java.nio.file.Files;
+import java.util.Map;
 
 @UtilityClass
 public class JaxbUtil {
@@ -26,6 +28,18 @@
         }
     }
 
+    public static UsersWithMeals process(File inputXml, Map<String, Object> params) throws IOException, JAXBException {
+        UsersWithMeals users = unmarshalAndFilter(inputXml, params);
+        System.out.println("JAXB processing completed successfully");
+        return users;
+    }
+
+    public static UsersWithMeals unmarshalAndFilter(File inputXml, Map<String, Object> params) throws IOException, JAXBException {
+        UsersWithMeals users = unmarshal(inputXml);
+        MealsUtil.filterAndAddExcess(users, params);
+        return users;
+    }
+
     public static UsersWithMeals unmarshal(File inputXml) throws IOException, JAXBException {
         try (Reader reader = Files.newBufferedReader(inputXml.toPath())) {
             return unmarshaller.unmarshal(reader);
Index: src/main/java/ru/javaops/docjava/util/Util.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/ru/javaops/docjava/util/Util.java b/src/main/java/ru/javaops/docjava/util/Util.java
new file mode 100644
--- /dev/null	(date 1695982990520)
+++ b/src/main/java/ru/javaops/docjava/util/Util.java	(date 1695982990520)
@@ -0,0 +1,34 @@
+package ru.javaops.docjava.util;
+
+import lombok.experimental.UtilityClass;
+import org.springframework.lang.Nullable;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@UtilityClass
+public class Util {
+    public static <T extends Comparable<T>> boolean isBetweenHalfOpen(T value, @Nullable T start, @Nullable T end) {
+        return (start == null || value.compareTo(start) >= 0) && (end == null || value.compareTo(end) < 0);
+    }
+
+    public static Map<String, Object> parseParams(List<String> params) {
+        Map<String, Object> map = new HashMap<>();
+        for (String parameter : params) {
+            String[] param = parameter.split("=");
+            String key = param[0].trim();
+            String value = param[1].trim();
+            if (key.endsWith("Date")) {
+                map.put(key, LocalDate.parse(value));
+            } else if (key.endsWith("Time")) {
+                map.put(key, LocalTime.parse(value));
+            } else {
+                throw new IllegalArgumentException("Unknown filter parameter: '" + key + "'");
+            }
+        }
+        return map;
+    }
+}
Index: src/main/java/ru/javaops/docjava/util/MealsUtil.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/ru/javaops/docjava/util/MealsUtil.java b/src/main/java/ru/javaops/docjava/util/MealsUtil.java
new file mode 100644
--- /dev/null	(date 1695982990517)
+++ b/src/main/java/ru/javaops/docjava/util/MealsUtil.java	(date 1695982990517)
@@ -0,0 +1,50 @@
+package ru.javaops.docjava.util;
+
+import lombok.experimental.UtilityClass;
+import org.springframework.lang.Nullable;
+import ru.javaops.docjava.schema.Meal;
+import ru.javaops.docjava.schema.Meals;
+import ru.javaops.docjava.schema.User;
+import ru.javaops.docjava.schema.UsersWithMeals;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@UtilityClass
+public class MealsUtil {
+
+    public static void filterAndAddExcess(UsersWithMeals users, Map<String, Object> params) {
+        users.getUsers().getUser().forEach(user -> MealsUtil.filterAndAddExcess(user, params));
+    }
+
+    public static void filterAndAddExcess(User user, Map<String, Object> params) {
+        if (user.getMeals() != null) {
+            List<Meal> meals = filterAndAddExcess(user.getMeals().getMeal(), user.getCaloriesPerDay(),
+                    (LocalDate) params.get("startDate"), (LocalDate) params.get("endDate"),
+                    (LocalTime) params.get("startTime"), (LocalTime) params.get("endTime"));
+            user.setMeals(new Meals(meals));
+        }
+    }
+
+    public static List<Meal> filterAndAddExcess(List<Meal> meals, int caloriesPerDay,
+                                                @Nullable LocalDate startDate, @Nullable LocalDate endDate,
+                                                @Nullable LocalTime startTime, @Nullable LocalTime endTime) {
+
+        LocalDate adaptStartDate = (startDate == null ? LocalDate.MIN : startDate);
+        LocalDate adaptEndDate = (endDate == null || endDate.equals(LocalDate.MAX) ? LocalDate.MAX : endDate.plusDays(1));
+
+        List<Meal> mealsFilteredByDate = meals.stream()
+                .filter(meal -> Util.isBetweenHalfOpen(meal.getDate(), adaptStartDate, adaptEndDate)).toList();
+
+        Map<LocalDate, Integer> caloriesSumByDate = mealsFilteredByDate.stream()
+                .collect(Collectors.groupingBy(Meal::getDate, Collectors.summingInt(Meal::getCalories)));
+
+        return mealsFilteredByDate.stream()
+                .filter(meal -> Util.isBetweenHalfOpen(meal.getTime(), startTime, endTime))
+                .peek(meal -> meal.setExcess(caloriesSumByDate.get(meal.getDate()) > caloriesPerDay))
+                .toList();
+    }
+}
Index: src/main/java/ru/javaops/docjava/Commands.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/ru/javaops/docjava/Commands.java b/src/main/java/ru/javaops/docjava/Commands.java
--- a/src/main/java/ru/javaops/docjava/Commands.java	(revision 42b9e1ab07bf421841ee5e99a7249ba165232968)
+++ b/src/main/java/ru/javaops/docjava/Commands.java	(date 1695983014721)
@@ -13,6 +13,8 @@
 import java.io.IOException;
 import java.util.List;
 
+import static ru.javaops.docjava.util.Util.parseParams;
+
 @ShellComponent
 public class Commands {
     @ShellMethod(key = "xsd", value = "Validate XML file against XSD (XML Schema)")
@@ -26,8 +28,7 @@
             @ShellOption(value = {"input", "-i"}, help = "Input file") File inputFile,
             @ShellOption(value = {"filter", "-f"}, help = "Filter params", defaultValue = "") List<String> params,
             @ShellOption(value = {"output", "-o"}, help = "Output file") File outputFile) throws JAXBException, IOException {
-        UsersWithMeals users = JaxbUtil.unmarshal(inputFile);
-        System.out.println(users);
+        JaxbUtil.process(inputFile, parseParams(params));
     }
 
     @ShellMethod(key = "stax", value = "Process XML file via StAX")
Index: src/main/java/ru/javaops/docjava/schema/Meal.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/ru/javaops/docjava/schema/Meal.java b/src/main/java/ru/javaops/docjava/schema/Meal.java
--- a/src/main/java/ru/javaops/docjava/schema/Meal.java	(revision 42b9e1ab07bf421841ee5e99a7249ba165232968)
+++ b/src/main/java/ru/javaops/docjava/schema/Meal.java	(date 1695982990513)
@@ -6,7 +6,9 @@
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 
 @XmlAccessorType(XmlAccessType.FIELD)
 @XmlRootElement(name = "Meal", namespace = "http://javaops.ru")
@@ -23,4 +25,12 @@
     protected LocalDateTime dateTime;
     @XmlAttribute(name = "excess")
     protected Boolean excess;
+
+    public LocalDate getDate() {
+        return dateTime.toLocalDate();
+    }
+
+    public LocalTime getTime() {
+        return dateTime.toLocalTime();
+    }
 }
