Реализована загрузка бд и её валидация

This commit is contained in:
Ivan Zinchenko 2024-03-29 22:34:22 +03:00
parent 6cc251551b
commit 94ccc698a7
Signed by: zinch
GPG Key ID: 6D45AA2C8FD6A37E
20 changed files with 725 additions and 15 deletions

5
.gitignore vendored
View File

@ -28,4 +28,7 @@ bin/
.vscode/
### Mac OS ###
.DS_Store
.DS_Store
### Build ###
target

View File

@ -1,3 +0,0 @@
Manifest-Version: 1.0
Created-By: zinch
Main-class: Main

View File

@ -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

4
Notes.md Normal file
View File

@ -0,0 +1,4 @@
### Этапы работы программы
1. Проверка файла с базой данных
2. Пользовательский ввод

99
README.md Normal file
View File

@ -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
}
```

117
db.xml Normal file
View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Products>
<Product>
<id>1</id>
<name>Product 1</name>
<coordinates>
<x>100</x>
<y>50</y>
</coordinates>
<creationDate>2024-03-01T12:00:00Z</creationDate>
<price>50</price>
<partNumber>PN123</partNumber>
<manufactureCost>30</manufactureCost>
<unitOfMeasure>KILOGRAMS</unitOfMeasure>
<owner>
<name>John Doe</name>
<passportID>AB12345</passportID>
<hairColor>BLACK</hairColor>
<location>
<x>10.5</x>
<y>20</y>
<name>Home</name>
</location>
</owner>
</Product>
<Product>
<id>2</id>
<name>Product 2</name>
<coordinates>
<x>200</x>
<y>100</y>
</coordinates>
<creationDate>2024-03-01T12:05:00Z</creationDate>
<price>80</price>
<partNumber>PN456</partNumber>
<manufactureCost>60</manufactureCost>
<unitOfMeasure>CENTIMETERS</unitOfMeasure>
<owner>
<name>Alice Smith</name>
<passportID>CD67890</passportID>
<hairColor>BLUE</hairColor>
<location>
<x>20.8</x>
<y>30</y>
<name>Office</name>
</location>
</owner>
</Product>
<Product>
<id>3</id>
<name>Product 3</name>
<coordinates>
<x>150</x>
<y>-200</y>
</coordinates>
<creationDate>2024-03-01T12:10:00Z</creationDate>
<price>120</price>
<manufactureCost>90</manufactureCost>
<unitOfMeasure>GRAMS</unitOfMeasure>
<owner>
<name>Emma Johnson</name>
<passportID>EF24680</passportID>
<hairColor>GREEN</hairColor>
<location>
<x>30.2</x>
<y>40</y>
<name>Warehouse</name>
</location>
</owner>
</Product>
<Product>
<id>4</id>
<name>Product 4</name>
<coordinates>
<x>300</x>
<y>-300</y>
</coordinates>
<creationDate>2024-03-01T12:15:00Z</creationDate>
<price>200</price>
<partNumber>PN789</partNumber>
<manufactureCost>150</manufactureCost>
<unitOfMeasure>KILOGRAMS</unitOfMeasure>
<owner>
<name>Bob Brown</name>
<passportID>GH13579</passportID>
<hairColor>ORANGE</hairColor>
<location>
<x>40.6</x>
<y>50</y>
<name>Factory</name>
</location>
</owner>
</Product>
<Product>
<id>5</id>
<name>Product 5</name>
<coordinates>
<x>400</x>
<y>400</y>
</coordinates>
<creationDate>2024-03-01T12:20:00Z</creationDate>
<price>150</price>
<partNumber>PN246</partNumber>
<manufactureCost>100</manufactureCost>
<unitOfMeasure>CENTIMETERS</unitOfMeasure>
<owner>
<name>Grace Lee</name>
<passportID>IJ35791</passportID>
<hairColor>WHITE</hairColor>
<location>
<x>50.9</x>
<y>60</y>
<name>Store</name>
</location>
</owner>
</Product>
</Products>

51
pom.xml Normal file
View File

@ -0,0 +1,51 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.zinch</groupId>
<artifactId>Lab5</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Lab5</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.17.0</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>5.0.0-M1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,5 +0,0 @@
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
}
}

View File

@ -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<Product> productList = DbController.loadDb();
if (productList != null) {
System.out.println(Validators.validateProductList(productList));
} else {
System.exit(1);
}
}
}

View File

@ -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);
}

View File

@ -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<Product> products;
public List<Product> getProducts() {
return products;
}
}
public static List<Product> loadDb() {
Map<String, String> 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;
}
}

View File

@ -0,0 +1,9 @@
package me.zinch.models;
public enum Color {
GREEN,
BLACK,
BLUE,
ORANGE,
WHITE
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,7 @@
package me.zinch.models;
public enum UnitOfMeasure {
KILOGRAMS,
CENTIMETERS,
GRAMS
}

View File

@ -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 {};
}

View File

@ -0,0 +1,11 @@
package me.zinch.validator;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
public class NotEmptyValidator implements ConstraintValidator<NotEmpty, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return value == null || !value.trim().isEmpty();
}
}

View File

@ -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<ConstraintViolation<Product>> violations = validator.validate(product);
for (ConstraintViolation<Product> violation : violations) {
System.out.println(violation.getMessage());
}
return violations.isEmpty();
}
public static boolean validateProductList(List<Product> db) {
return db.stream().allMatch(Validators::validateProduct);
}
}