diff --git a/.gitignore b/.gitignore
index 82b7e88..1771f2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,7 @@ bin/
.vscode/
### Mac OS ###
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+### Build ###
+target
\ No newline at end of file
diff --git a/MANIFEST.mf b/MANIFEST.mf
deleted file mode 100644
index 6f7f37a..0000000
--- a/MANIFEST.mf
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-Created-By: zinch
-Main-class: Main
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 4effb7d..0000000
--- a/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-all:
- rm -rf target
- javac -d target -sourcepath src src/Main.java src/**/*
- cd target; jar cfm app.jar ../MANIFEST.mf *.class ./**/*.class
- mv target/app.jar ./
- rm -rf target
diff --git a/Notes.md b/Notes.md
new file mode 100644
index 0000000..faf35ae
--- /dev/null
+++ b/Notes.md
@@ -0,0 +1,4 @@
+### Этапы работы программы
+
+1. Проверка файла с базой данных
+2. Пользовательский ввод
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..94b0889
--- /dev/null
+++ b/README.md
@@ -0,0 +1,99 @@
+# Лабораторная работа №5
+
+## Выполнил
+> ФИ: Зинченко Иван
+>
+> ИСУ: 408657
+>
+> Вариант: 1632
+
+## Задание
+Реализовать консольное приложение, которое реализует управление коллекцией объектов в интерактивном режиме.
+В коллекции необходимо хранить объекты класса `Product`, описание которого приведено ниже.
+
+Разработанная программа должна удовлетворять следующим требованиям:
+- Класс, коллекцией экземпляров которого управляет программа, должен реализовывать сортировку по умолчанию.
+- Все требования к полям класса (указанные в виде комментариев) должны быть выполнены.
+- Для хранения необходимо использовать коллекцию типа `java.util.TreeSet`
+- При запуске приложения коллекция должна автоматически заполняться значениями из файла.
+- Имя файла должно передаваться программе с помощью: переменная окружения.
+- Данные должны храниться в файле в формате `xml`
+- Чтение данных из файла необходимо реализовать с помощью класса `java.io.BufferedInputStream`
+- Запись данных в файл необходимо реализовать с помощью класса `java.io.OutputStreamWriter`
+- Все классы в программе должны быть задокументированы в формате javadoc.
+- Программа должна корректно работать с неправильными данными (ошибки пользовательского ввода, отсутсвие прав доступа к файлу и т.п.).
+
+В интерактивном режиме программа должна поддерживать выполнение следующих команд:
+- `help` : вывести справку по доступным командам
+- `info` : вывести в стандартный поток вывода информацию о коллекции (тип, дата инициализации, количество элементов и т.д.)
+- `show` : вывести в стандартный поток вывода все элементы коллекции в строковом представлении
+- `add {element}` : добавить новый элемент в коллекцию
+- `update id {element}` : обновить значение элемента коллекции, id которого равен заданному
+- `remove_by_id id` : удалить элемент из коллекции по его id
+- `clear` : очистить коллекцию
+- `save` : сохранить коллекцию в файл
+- `execute_script file_name` : считать и исполнить скрипт из указанного файла. В скрипте содержатся команды в таком же виде, в котором их вводит пользователь в интерактивном режиме.
+- `exit` : завершить программу (без сохранения в файл)
+- `add_if_max {element}` : добавить новый элемент в коллекцию, если его значение превышает значение наибольшего элемента этой коллекции
+- `add_if_min {element}` : добавить новый элемент в коллекцию, если его значение меньше, чем у наименьшего элемента этой коллекции
+- `remove_lower {element}` : удалить из коллекции все элементы, меньшие, чем заданный
+- `filter_contains_name name` : вывести элементы, значение поля name которых содержит заданную подстроку
+- `print_ascending` : вывести элементы коллекции в порядке возрастания
+- `print_unique_manufacture_cost` : вывести уникальные значения поля manufactureCost всех элементов в коллекции
+
+Формат ввода команд:
+- Все аргументы команды, являющиеся стандартными типами данных (примитивные типы, классы-оболочки, String, классы для хранения дат), должны вводиться в той же строке, что и имя команды.
+- Все составные типы данных (объекты классов, хранящиеся в коллекции) должны вводиться по одному полю в строку.
+- При вводе составных типов данных пользователю должно показываться приглашение к вводу, содержащее имя поля (например, "Введите дату рождения:")
+- Если поле является enum'ом, то вводится имя одной из его констант (при этом список констант должен быть предварительно выведен).
+- При некорректном пользовательском вводе (введена строка, не являющаяся именем константы в enum'е; введена строка вместо числа; введённое число не входит в указанные границы и т.п.) должно быть показано сообщение об ошибке и предложено повторить ввод поля.
+- Для ввода значений null использовать пустую строку.
+- Поля с комментарием "Значение этого поля должно генерироваться автоматически" не должны вводиться пользователем вручную при добавлении.
+
+Описание хранимых в коллекции классов:
+
+```java
+public class Product {
+ private Long id; //Поле не может быть null, Значение поля должно быть больше 0, Значение этого поля должно быть уникальным, Значение этого поля должно генерироваться автоматически
+ private String name; //Поле не может быть null, Строка не может быть пустой
+ private Coordinates coordinates; //Поле не может быть null
+ private java.time.ZonedDateTime creationDate; //Поле не может быть null, Значение этого поля должно генерироваться автоматически
+ private Long price; //Поле не может быть null, Значение поля должно быть больше 0
+ private String partNumber; //Длина строки не должна быть больше 82, Значение этого поля должно быть уникальным, Строка не может быть пустой, Поле может быть null
+ private long manufactureCost;
+ private UnitOfMeasure unitOfMeasure; //Поле не может быть null
+ private Person owner; //Поле не может быть null
+}
+
+public class Coordinates {
+ private long x; //Максимальное значение поля: 883
+ private Long y; //Значение поля должно быть больше -427, Поле не может быть null
+}
+
+public class Person {
+ private String name; //Поле не может быть null, Строка не может быть пустой
+ private String passportID; //Длина строки должна быть не меньше 5, Длина строки не должна быть больше 22, Поле не может быть null
+ private Color hairColor; //Поле может быть null
+ private Location location; //Поле может быть null
+}
+
+public class Location {
+ private float x;
+ private Integer y; //Поле не может быть null
+ private String name; //Строка не может быть пустой, Поле может быть null
+}
+
+public enum UnitOfMeasure {
+ KILOGRAMS,
+ CENTIMETERS,
+ GRAMS
+}
+
+public enum Color {
+ GREEN,
+ BLACK,
+ BLUE,
+ ORANGE,
+ WHITE
+}
+```
diff --git a/db.xml b/db.xml
new file mode 100644
index 0000000..3885e88
--- /dev/null
+++ b/db.xml
@@ -0,0 +1,117 @@
+
+
+
+ 1
+ Product 1
+
+ 100
+ 50
+
+ 2024-03-01T12:00:00Z
+ 50
+ PN123
+ 30
+ KILOGRAMS
+
+ John Doe
+ AB12345
+ BLACK
+
+ 10.5
+ 20
+ Home
+
+
+
+
+ 2
+ Product 2
+
+ 200
+ 100
+
+ 2024-03-01T12:05:00Z
+ 80
+ PN456
+ 60
+ CENTIMETERS
+
+ Alice Smith
+ CD67890
+ BLUE
+
+ 20.8
+ 30
+ Office
+
+
+
+
+ 3
+ Product 3
+
+ 150
+ -200
+
+ 2024-03-01T12:10:00Z
+ 120
+ 90
+ GRAMS
+
+ Emma Johnson
+ EF24680
+ GREEN
+
+ 30.2
+ 40
+ Warehouse
+
+
+
+
+ 4
+ Product 4
+
+ 300
+ -300
+
+ 2024-03-01T12:15:00Z
+ 200
+ PN789
+ 150
+ KILOGRAMS
+
+ Bob Brown
+ GH13579
+ ORANGE
+
+ 40.6
+ 50
+ Factory
+
+
+
+
+ 5
+ Product 5
+
+ 400
+ 400
+
+ 2024-03-01T12:20:00Z
+ 150
+ PN246
+ 100
+ CENTIMETERS
+
+ Grace Lee
+ IJ35791
+ WHITE
+
+ 50.9
+ 60
+ Store
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..8a16eac
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ me.zinch
+ Lab5
+ 1.0
+ jar
+
+ Lab5
+
+
+ UTF-8
+
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ 2.15.0
+
+
+ com.fasterxml.jackson.datatype
+ jackson-datatype-jsr310
+ 2.17.0
+
+
+ org.hibernate.validator
+ hibernate-validator
+ 8.0.1.Final
+
+
+ org.glassfish
+ jakarta.el
+ 5.0.0-M1
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 17
+ 17
+
+
+
+
+
diff --git a/src/Main.java b/src/Main.java
deleted file mode 100644
index 962de4e..0000000
--- a/src/Main.java
+++ /dev/null
@@ -1,5 +0,0 @@
-public class Main {
- public static void main(String[] args) {
- System.out.println("Hello world!");
- }
-}
\ No newline at end of file
diff --git a/src/main/java/me/zinch/App.java b/src/main/java/me/zinch/App.java
new file mode 100644
index 0000000..ffc7edb
--- /dev/null
+++ b/src/main/java/me/zinch/App.java
@@ -0,0 +1,18 @@
+package me.zinch;
+
+import me.zinch.files.DbController;
+import me.zinch.models.Product;
+import me.zinch.validator.Validators;
+
+import java.util.List;
+
+public class App {
+ public static void main( String[] args ) {
+ List productList = DbController.loadDb();
+ if (productList != null) {
+ System.out.println(Validators.validateProductList(productList));
+ } else {
+ System.exit(1);
+ }
+ }
+}
diff --git a/src/main/java/me/zinch/commands/Command.java b/src/main/java/me/zinch/commands/Command.java
new file mode 100644
index 0000000..3cfbb80
--- /dev/null
+++ b/src/main/java/me/zinch/commands/Command.java
@@ -0,0 +1,29 @@
+package me.zinch.commands;
+
+public abstract class Command {
+ private final String name;
+ private final String description;
+ private final String pattern;
+
+ public Command(String name, String description, String pattern) {
+ this.name = name;
+ this.description = description;
+ this.pattern = pattern;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getPattern() {
+ return this.pattern;
+ }
+
+ public abstract void action();
+ public abstract void action(String param);
+ public abstract void action(String[] params);
+}
diff --git a/src/main/java/me/zinch/files/DbController.java b/src/main/java/me/zinch/files/DbController.java
new file mode 100644
index 0000000..3990224
--- /dev/null
+++ b/src/main/java/me/zinch/files/DbController.java
@@ -0,0 +1,41 @@
+package me.zinch.files;
+
+import com.fasterxml.jackson.dataformat.xml.XmlMapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
+import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import me.zinch.models.Product;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+public class DbController {
+ public static class Products {
+ @JacksonXmlElementWrapper(useWrapping = false)
+ @JacksonXmlProperty(localName = "Product")
+ private List products;
+
+ public List getProducts() {
+ return products;
+ }
+ }
+
+ public static List loadDb() {
+ Map env = System.getenv();
+ try {
+ BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(env.getOrDefault("DB_FILE", "")));
+ XmlMapper xmlMapper = XmlMapper.builder().addModule(new JavaTimeModule()).build();
+ Products collection = xmlMapper.readValue(bufferedInputStream, Products.class);
+ return collection.getProducts();
+ } catch (FileNotFoundException e) {
+ System.err.println("Ошибка! Файл не найден.");
+ } catch (IOException e) {
+ System.err.println("Произошла неожиданная ошибка во время работы с файлом!");
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/me/zinch/models/Color.java b/src/main/java/me/zinch/models/Color.java
new file mode 100644
index 0000000..883b36e
--- /dev/null
+++ b/src/main/java/me/zinch/models/Color.java
@@ -0,0 +1,9 @@
+package me.zinch.models;
+
+public enum Color {
+ GREEN,
+ BLACK,
+ BLUE,
+ ORANGE,
+ WHITE
+}
diff --git a/src/main/java/me/zinch/models/Coordinates.java b/src/main/java/me/zinch/models/Coordinates.java
new file mode 100644
index 0000000..e04f04c
--- /dev/null
+++ b/src/main/java/me/zinch/models/Coordinates.java
@@ -0,0 +1,40 @@
+package me.zinch.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.Objects;
+
+public class Coordinates {
+ @Max(value = 883, message = "Максимальное значение поля x: 883")
+ @JsonProperty("x")
+ private long x; //Максимальное значение поля: 883
+
+ @NotNull(message = "Поле не может быть null")
+ @Min(value = -427, message = "Значение поля y должно быть больше -427")
+ @JsonProperty("y")
+ private Long y; //Значение поля должно быть больше -427, Поле не может быть null
+
+ @Override
+ public String toString() {
+ return "Coordinates{" +
+ "x=" + x +
+ ", y=" + y +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Coordinates that = (Coordinates) o;
+ return x == that.x && Objects.equals(y, that.y);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y);
+ }
+}
diff --git a/src/main/java/me/zinch/models/Location.java b/src/main/java/me/zinch/models/Location.java
new file mode 100644
index 0000000..2b8bb4d
--- /dev/null
+++ b/src/main/java/me/zinch/models/Location.java
@@ -0,0 +1,54 @@
+package me.zinch.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+
+import java.util.Objects;
+
+public class Location {
+ @JsonProperty("x")
+ private float x;
+
+ @NotNull(message = "Поле y не может быть null")
+ @JsonProperty("y")
+ private Integer y; //Поле не может быть null
+
+ @NotBlank(message = "Строка name не может быть пустой или null")
+ @JsonProperty("name")
+ private String name; //Строка не может быть пустой, Поле может быть null
+
+ public float getX() {
+ return x;
+ }
+
+ public Integer getY() {
+ return y;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String toString() {
+ return "Location{" +
+ "x=" + x +
+ ", y=" + y +
+ ", name='" + name + '\'' +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Location location = (Location) o;
+ return Float.compare(x, location.x) == 0 && Objects.equals(y, location.y) && Objects.equals(name, location.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(x, y, name);
+ }
+}
diff --git a/src/main/java/me/zinch/models/Person.java b/src/main/java/me/zinch/models/Person.java
new file mode 100644
index 0000000..0341f6f
--- /dev/null
+++ b/src/main/java/me/zinch/models/Person.java
@@ -0,0 +1,50 @@
+package me.zinch.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+
+import java.util.Objects;
+
+public class Person {
+ @NotBlank(message = "Строка name не может быть пустой или null")
+ @JsonProperty("name")
+ private String name; //Поле не может быть null, Строка не может быть пустой
+
+ @NotNull(message = "Поле passportID не может быть null")
+ @Size(min = 5, max = 22, message = "Длина строки passportID должна быть не меньше 5 и не должна быть больше 22")
+ @JsonProperty("passportID")
+ private String passportID; //Длина строки должна быть не меньше 5, Длина строки не должна быть больше 22, Поле не может быть null
+
+ @NotNull(message = "Поле hairColor может быть null")
+ @JsonProperty("hairColor")
+ private Color hairColor; //Поле может быть null
+
+ @NotNull(message = "Поле location может быть null")
+ @JsonProperty("location")
+ private Location location; //Поле может быть null
+
+ @Override
+ public String toString() {
+ return "Person{" +
+ "name='" + name + '\'' +
+ ", passportID='" + passportID + '\'' +
+ ", hairColor=" + hairColor +
+ ", location=" + location +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Person person = (Person) o;
+ return Objects.equals(name, person.name) && Objects.equals(passportID, person.passportID) && hairColor == person.hairColor && Objects.equals(location, person.location);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, passportID, hairColor, location);
+ }
+}
diff --git a/src/main/java/me/zinch/models/Product.java b/src/main/java/me/zinch/models/Product.java
new file mode 100644
index 0000000..60108bb
--- /dev/null
+++ b/src/main/java/me/zinch/models/Product.java
@@ -0,0 +1,137 @@
+package me.zinch.models;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import me.zinch.validator.NotEmpty;
+
+import java.time.ZonedDateTime;
+import java.util.Objects;
+
+public class Product {
+ @NotNull(message = "Поле id не может быть null")
+ @Min(value = 1, message = "Значение поля id должно быть больше 0")
+ @JsonProperty("id")
+ private Long id; //Поле не может быть null, Значение поля должно быть больше 0, Значение этого поля должно быть уникальным, Значение этого поля должно генерироваться автоматически
+
+ @NotBlank(message = "Строка name не может быть пустой или null")
+ @JsonProperty("name")
+ private String name; //Поле не может быть null, Строка не может быть пустой
+
+ @NotNull(message = "Поле coordinates не может быть null ")
+ @JsonProperty("coordinates")
+ private Coordinates coordinates; //Поле не может быть null
+
+ @NotNull(message = "Поле creationDate не может быть null")
+ @JsonProperty("creationDate")
+ private ZonedDateTime creationDate; //Поле не может быть null, Значение этого поля должно генерироваться автоматически
+
+ @NotNull(message = "Поле price не может быть null")
+ @Min(value = 1, message = "Поле price не может быть null")
+ @JsonProperty("price")
+ private Long price; //Поле не может быть null, Значение поля должно быть больше 0
+
+ @NotEmpty(message = "Строка partNumber не может быть пустой")
+ @Size(max = 82, message = "Длина строки partNumber не должна быть больше 82")
+ @JsonProperty("partNumber")
+ private String partNumber; //Длина строки не должна быть больше 82, Значение этого поля должно быть уникальным, Строка не может быть пустой, Поле может быть null
+
+ @JsonProperty("manufactureCost")
+ private long manufactureCost;
+
+ @NotNull(message = "Поле unitOfMeasure не может быть null")
+ @JsonProperty("unitOfMeasure")
+ private UnitOfMeasure unitOfMeasure; //Поле не может быть null
+
+ @NotNull(message = "Поле owner не может быть null")
+ @JsonProperty("owner")
+ private Person owner; //Поле не может быть null
+
+ public Product() {}
+
+ public Product (Long id,
+ String name,
+ Coordinates coordinates,
+ Long price,
+ String partNumber,
+ long manufactureCost,
+ UnitOfMeasure unitOfMeasure,
+ Person owner
+ ) {
+ this.id = id;
+ this.name = name;
+ this.coordinates = coordinates;
+ this.creationDate = ZonedDateTime.now();
+ this.price = price;
+ this.partNumber = partNumber;
+ this.manufactureCost = manufactureCost;
+ this.unitOfMeasure = unitOfMeasure;
+ this.owner = owner;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Coordinates getCoordinates() {
+ return coordinates;
+ }
+
+ public ZonedDateTime getCreationDate() {
+ return creationDate;
+ }
+
+ public Long getPrice() {
+ return price;
+ }
+
+ public String getPartNumber() {
+ return partNumber;
+ }
+
+ public long getManufactureCost() {
+ return manufactureCost;
+ }
+
+ public UnitOfMeasure getUnitOfMeasure() {
+ return unitOfMeasure;
+ }
+
+ public Person getOwner() {
+ return owner;
+ }
+
+ @Override
+ public String toString() {
+ return "Product{" +
+ "id=" + id +
+ ", name='" + name + '\'' +
+ ", coordinates=" + coordinates +
+ ", creationDate=" + creationDate +
+ ", price=" + price +
+ ", partNumber='" + partNumber + '\'' +
+ ", manufactureCost=" + manufactureCost +
+ ", unitOfMeasure=" + unitOfMeasure +
+ ", owner=" + owner +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Product product = (Product) o;
+ return manufactureCost == product.manufactureCost && Objects.equals(id, product.id) && Objects.equals(name, product.name) && Objects.equals(coordinates, product.coordinates) && Objects.equals(creationDate, product.creationDate) && Objects.equals(price, product.price) && Objects.equals(partNumber, product.partNumber) && unitOfMeasure == product.unitOfMeasure && Objects.equals(owner, product.owner);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, name, coordinates, creationDate, price, partNumber, manufactureCost, unitOfMeasure, owner);
+ }
+}
diff --git a/src/main/java/me/zinch/models/UnitOfMeasure.java b/src/main/java/me/zinch/models/UnitOfMeasure.java
new file mode 100644
index 0000000..18073b1
--- /dev/null
+++ b/src/main/java/me/zinch/models/UnitOfMeasure.java
@@ -0,0 +1,7 @@
+package me.zinch.models;
+
+public enum UnitOfMeasure {
+ KILOGRAMS,
+ CENTIMETERS,
+ GRAMS
+}
diff --git a/src/main/java/me/zinch/validator/NotEmpty.java b/src/main/java/me/zinch/validator/NotEmpty.java
new file mode 100644
index 0000000..04bbb2a
--- /dev/null
+++ b/src/main/java/me/zinch/validator/NotEmpty.java
@@ -0,0 +1,18 @@
+package me.zinch.validator;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Constraint(validatedBy = NotEmptyValidator.class)
+public @interface NotEmpty {
+ String message() default "Value must not be blank";
+ Class>[] groups() default {};
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/me/zinch/validator/NotEmptyValidator.java b/src/main/java/me/zinch/validator/NotEmptyValidator.java
new file mode 100644
index 0000000..21d22b2
--- /dev/null
+++ b/src/main/java/me/zinch/validator/NotEmptyValidator.java
@@ -0,0 +1,11 @@
+package me.zinch.validator;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+public class NotEmptyValidator implements ConstraintValidator {
+ @Override
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ return value == null || !value.trim().isEmpty();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/me/zinch/validator/Validators.java b/src/main/java/me/zinch/validator/Validators.java
new file mode 100644
index 0000000..01b2f26
--- /dev/null
+++ b/src/main/java/me/zinch/validator/Validators.java
@@ -0,0 +1,36 @@
+package me.zinch.validator;
+
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
+import jakarta.validation.ValidatorFactory;
+import me.zinch.models.Product;
+import org.hibernate.validator.HibernateValidator;
+
+import java.util.List;
+import java.util.Set;
+
+public class Validators {
+ private static final Validator validator;
+
+ static {
+ ValidatorFactory factory = Validation.byProvider(HibernateValidator.class)
+ .configure()
+ .showValidatedValuesInTraceLogs(false)
+ .buildValidatorFactory();
+ validator = factory.usingContext().getValidator();
+ factory.close();
+ }
+
+ public static boolean validateProduct(Product product) {
+ Set> violations = validator.validate(product);
+ for (ConstraintViolation violation : violations) {
+ System.out.println(violation.getMessage());
+ }
+ return violations.isEmpty();
+ }
+
+ public static boolean validateProductList(List db) {
+ return db.stream().allMatch(Validators::validateProduct);
+ }
+}