Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ac2784373 | |||
| cd428e532b | |||
| f3e6257e8e | |||
| afc63b0772 | |||
| ca28cbab7d | |||
| 96a055a29d | |||
| f00ba84c10 | |||
| a630bf28f8 | |||
| d33c983dbb | |||
| d4fe517eb8 | |||
| 722b9f4d8c |
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@ -0,0 +1,27 @@
|
||||
#
|
||||
# Build stage
|
||||
#
|
||||
FROM maven:3.9.3-eclipse-temurin-17-alpine AS build
|
||||
WORKDIR /home/app
|
||||
COPY pom.xml .
|
||||
RUN mvn dependency:go-offline
|
||||
COPY src ./src
|
||||
RUN mvn clean javadoc:javadoc package
|
||||
|
||||
#
|
||||
# Generate index.html
|
||||
#
|
||||
FROM pandoc/core:3.1-ubuntu as mainPage
|
||||
WORKDIR /home/app
|
||||
COPY README.md .
|
||||
RUN pandoc -s -o index.html README.md
|
||||
|
||||
#
|
||||
# Package stage
|
||||
#
|
||||
FROM nginx:stable-alpine
|
||||
COPY --from=build /home/app/target/site /usr/share/nginx/html
|
||||
COPY --from=build /home/app/target/Lab5-1.0-jar-with-dependencies.jar /usr/share/nginx/html/app.jar
|
||||
COPY --from=mainPage /home/app/index.html /usr/share/nginx/html
|
||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
||||
39
README.md
39
README.md
@ -7,47 +7,86 @@
|
||||
>
|
||||
> Вариант: 1632
|
||||
|
||||
## Результат
|
||||
|
||||
- [Ссылка на файл](/app.jar)
|
||||
|
||||
- [Документация](/apidocs/index.html)
|
||||
|
||||
## Задание
|
||||
Реализовать консольное приложение, которое реализует управление коллекцией объектов в интерактивном режиме.
|
||||
В коллекции необходимо хранить объекты класса `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 использовать пустую строку.
|
||||
|
||||
- Поля с комментарием "Значение этого поля должно генерироваться автоматически" не должны вводиться пользователем вручную при добавлении.
|
||||
|
||||
Описание хранимых в коллекции классов:
|
||||
|
||||
1
db.xml
1
db.xml
@ -1 +0,0 @@
|
||||
<Products><Product><id>341</id><name>ewreqr</name><coordinates><x>1432</x><y>132432</y></coordinates><creationDate>1711875600.000000000</creationDate><price>12324</price><partNumber>3214</partNumber><manufactureCost>342</manufactureCost><unitOfMeasure>CENTIMETERS</unitOfMeasure><owner><name>324324</name><passportID>32143214</passportID><hairColor>WHITE</hairColor><location><x>342.0</x><y>423</y><name>214</name></location></owner></Product><Product><id>342</id><name>Hello</name><coordinates><x>13</x><y>4</y></coordinates><creationDate>1713267558.813808800</creationDate><price>12312</price><partNumber>312</partNumber><manufactureCost>321</manufactureCost><unitOfMeasure>CENTIMETERS</unitOfMeasure><owner><name>123</name><passportID>431</passportID><hairColor>BLACK</hairColor><location><x>2321.0</x><y>432</y><name>31</name></location></owner></Product></Products>
|
||||
62
nginx.conf
Normal file
62
nginx.conf
Normal file
@ -0,0 +1,62 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
pid /run/nginx.pid;
|
||||
include /etc/nginx/modules-enabled/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 768;
|
||||
# multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
##
|
||||
# Basic Settings
|
||||
##
|
||||
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
types_hash_max_size 2048;
|
||||
# server_tokens off;
|
||||
|
||||
# server_names_hash_bucket_size 64;
|
||||
# server_name_in_redirect off;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
##
|
||||
# SSL Settings
|
||||
##
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
##
|
||||
# Logging Settings
|
||||
##
|
||||
|
||||
#access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
##
|
||||
# Gzip Settings
|
||||
##
|
||||
|
||||
gzip on;
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
'' '';
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location / {
|
||||
try_files $uri /index.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
5
pom.xml
5
pom.xml
@ -36,6 +36,11 @@
|
||||
<artifactId>jakarta.el</artifactId>
|
||||
<version>5.0.0-M1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.23.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -25,7 +25,7 @@ public class Add extends Command {
|
||||
var product = productCollection.addProduct(productDTO);
|
||||
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
throw new CommandActionException(String.format("Произошла ошибка при добавлении%n%s", e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,21 +14,26 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class AddIfMax extends Command {
|
||||
public AddIfMax() {
|
||||
super("add_if_max {element}", "добавить новый элемент в коллекцию, если его значение цены превышает значение наибольшей цены этой коллекции", Pattern.compile("^add_if_max \\d+"));
|
||||
super("add_if_max {element}", "добавить новый элемент в коллекцию, если его значение цены превышает значение наибольшей цены этой коллекции", Pattern.compile("^add_if_max .+"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) throws CommandActionException {
|
||||
var productDTO = Console.readProductDTO();
|
||||
try {
|
||||
Validators.validateObject(productDTO).throwIfNotValid();
|
||||
if (productCollection.getMaxPrice() < productDTO.getPrice()) {
|
||||
var product = productCollection.addProduct(productDTO);
|
||||
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
var productDTO = Console.readProductDTO();
|
||||
try {
|
||||
Validators.validateObject(productDTO).throwIfNotValid();
|
||||
if (productCollection.getMaxPrice() == null ||productCollection.getMaxPrice() < productDTO.getPrice()) {
|
||||
var product = productCollection.addProduct(productDTO);
|
||||
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
|
||||
}
|
||||
return "Продукт не подходит под условие";
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
}
|
||||
return "Продукт не подходит под условие";
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
} catch (NumberFormatException e) {
|
||||
return "Неверный аргумент, id может быть только число";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,16 +19,21 @@ public class AddIfMin extends Command {
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
var productDTO = Console.readProductDTO();
|
||||
try {
|
||||
Validators.validateObject(productDTO).throwIfNotValid();
|
||||
if (productCollection.getMinPrice() > productDTO.getPrice()) {
|
||||
var product = productCollection.addProduct(productDTO);
|
||||
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
|
||||
Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
var productDTO = Console.readProductDTO();
|
||||
try {
|
||||
Validators.validateObject(productDTO).throwIfNotValid();
|
||||
if (productCollection.getMinPrice() == null || productCollection.getMinPrice() > productDTO.getPrice()) {
|
||||
var product = productCollection.addProduct(productDTO);
|
||||
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
|
||||
}
|
||||
return "Продукт не подходит под условие";
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
}
|
||||
return "Продукт не подходит под условие";
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
} catch (NumberFormatException e) {
|
||||
return "Неверный аргумент, id может быть только число";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,9 @@ public class ExecuteScript extends Command {
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
try {
|
||||
var result = new StringBuilder();
|
||||
var scriptFile = Console.getLastCommand().split(" ")[1];
|
||||
result.append(String.format("Скрипт %s начинает работу.", scriptFile)).append("\n");
|
||||
addFileToStack(scriptFile);
|
||||
var commandList = ScriptLoader.loadList(scriptFile);
|
||||
for (var input: commandList) {
|
||||
@ -47,13 +49,14 @@ public class ExecuteScript extends Command {
|
||||
if (checkFileInStack(nextFile)) continue;
|
||||
addFileToStack(nextFile);
|
||||
}
|
||||
command.get().action(productCollection);
|
||||
result.append(command.get().action(productCollection)).append("\n");
|
||||
} else {
|
||||
new UnknownCommand().action(productCollection);
|
||||
}
|
||||
}
|
||||
clearStack();
|
||||
return String.format("Скрипт %s завершил работу.%n", scriptFile);
|
||||
result.append(String.format("Скрипт %s завершил работу.", scriptFile)).append("\n");
|
||||
return result.toString();
|
||||
} catch (IOException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ public class Exit extends Command {
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
Console.stopApp();
|
||||
Console.stopAppWithoutSaving();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ public class FilterContainsName extends Command {
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
String name = Console.getLastCommand().split(" ")[1];
|
||||
return productCollection.filterContainsName(name);
|
||||
var result = productCollection.filterContainsName(name);
|
||||
return result.isEmpty() ? "Таких элементов не найдено" : result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ public class PrintAscending extends Command {
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
return productCollection.toString();
|
||||
var result = productCollection.toString();
|
||||
return result.isEmpty() ? "Коллекция пуста" : result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ public class PrintUniqueManufactureCost extends Command {
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
return productCollection.getUniqueManufactureCost();
|
||||
var result = productCollection.getUniqueManufactureCost();
|
||||
return result.isEmpty() ? "Коллекция пуста" : result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package me.zinch.commands;
|
||||
|
||||
import me.zinch.console.Console;
|
||||
import me.zinch.exceptions.CommandActionException;
|
||||
import me.zinch.wrapper.ProductCollection;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
@ -10,13 +11,20 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class Remove extends Command {
|
||||
public Remove() {
|
||||
super("remove_by_id id", "удалить элемент из коллекции по его id", Pattern.compile("^remove_by_id \\d+"));
|
||||
super("remove_by_id id", "удалить элемент из коллекции по его id", Pattern.compile("^remove_by_id .+"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
var product = productCollection.removeProduct(id);
|
||||
return String.format("Продукт %s был удалён", product.getName());
|
||||
public String action(ProductCollection productCollection) throws CommandActionException {
|
||||
try {
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
if (productCollection.isProductIdExists(id)) {
|
||||
var product = productCollection.removeProduct(id);
|
||||
return String.format("Продукт %s был удалён", product.getName());
|
||||
}
|
||||
return "Продукта с таким id не существует";
|
||||
} catch (NumberFormatException e) {
|
||||
return "Неверный аргумент, id может быть только число";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,12 @@ public class RemoveLower extends Command {
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
var size = productCollection.removeLover(id);
|
||||
return String.format("Было удалено %d продуктов", size);
|
||||
try {
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
var size = productCollection.removeLover(id);
|
||||
return String.format("Было удалено %d продуктов", size);
|
||||
} catch (NumberFormatException e) {
|
||||
return "Неверный аргумент, id может быть только число";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ public class Show extends Command {
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) {
|
||||
return productCollection.toString();
|
||||
var result = productCollection.toString();
|
||||
return result.isEmpty() ? "Коллекция пуста" : result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,22 +13,26 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public class Update extends Command {
|
||||
public Update() {
|
||||
super("update id {element}", "обновить значение элемента коллекции, id которого равен заданному", Pattern.compile("^update \\d+"));
|
||||
super("update id {element}", "обновить значение элемента коллекции, id которого равен заданному", Pattern.compile("^update .+"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String action(ProductCollection productCollection) throws CommandActionException {
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
if (productCollection.isProductIdExists(id)) {
|
||||
var productDTO = Console.readProductDTO();
|
||||
try {
|
||||
Validators.validateObject(productDTO).throwIfNotValid();
|
||||
var product = productCollection.updateProduct(id, productDTO);
|
||||
return String.format("Продукт %s был изменён", product.getName());
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
try {
|
||||
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
|
||||
if (productCollection.isProductIdExists(id)) {
|
||||
var productDTO = Console.readProductDTO();
|
||||
try {
|
||||
Validators.validateObject(productDTO).throwIfNotValid();
|
||||
var product = productCollection.updateProduct(id, productDTO);
|
||||
return String.format("Продукт %s был изменён", product.getName());
|
||||
} catch (ValidationException e) {
|
||||
throw new CommandActionException(e.getMessage());
|
||||
}
|
||||
}
|
||||
return "Продукта с таким id не существует";
|
||||
} catch (NumberFormatException e) {
|
||||
return "Неверный аргумент, id может быть только число";
|
||||
}
|
||||
return "Продукта с таким id не существует";
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ package me.zinch.console;
|
||||
import me.zinch.commands.CommandManager;
|
||||
import me.zinch.exceptions.ColorFormatException;
|
||||
import me.zinch.exceptions.CommandActionException;
|
||||
import me.zinch.exceptions.IllegalProductDtoException;
|
||||
import me.zinch.exceptions.UnitOfMeasureFormatException;
|
||||
import me.zinch.exceptions.ValidationException;
|
||||
import me.zinch.files.DbController;
|
||||
@ -11,6 +10,7 @@ import me.zinch.models.Color;
|
||||
import me.zinch.models.Coordinates;
|
||||
import me.zinch.models.Location;
|
||||
import me.zinch.models.Person;
|
||||
import me.zinch.models.Product;
|
||||
import me.zinch.models.ProductDTO;
|
||||
import me.zinch.models.UnitOfMeasure;
|
||||
import me.zinch.validator.ValidateResult;
|
||||
@ -19,9 +19,9 @@ import me.zinch.wrapper.ProductCollection;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IllegalFormatConversionException;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
@ -32,14 +32,39 @@ public class Console {
|
||||
private static final Scanner scanner = new Scanner(System.in);
|
||||
private static boolean isRunning = true;
|
||||
private static final List<String> history = new ArrayList<>();
|
||||
private static boolean isExitMessageShowed = false;
|
||||
|
||||
private static ProductCollection productCollection;
|
||||
|
||||
public static void appendHistory(String input) {
|
||||
history.add(input);
|
||||
}
|
||||
|
||||
public static void stopApp() {
|
||||
public static void log(String msg) {
|
||||
if (isRunning) System.out.println(msg);
|
||||
}
|
||||
|
||||
public static void log() {
|
||||
log("");
|
||||
}
|
||||
|
||||
public static void stopAppWithoutSaving() {
|
||||
showExitMessage();
|
||||
isRunning = false;
|
||||
scanner.close();
|
||||
}
|
||||
|
||||
public static void stopAppWithSaving() {
|
||||
try {
|
||||
DbController.saveDb(productCollection.toList());
|
||||
} catch (IOException e) {
|
||||
log(e.getMessage());
|
||||
}
|
||||
stopAppWithoutSaving();
|
||||
}
|
||||
|
||||
public static void showExitMessage() {
|
||||
if (!isExitMessageShowed) log("\nВсего хорошего!");
|
||||
isExitMessageShowed = true;
|
||||
}
|
||||
|
||||
public static String getLastCommand() {
|
||||
@ -48,16 +73,18 @@ public class Console {
|
||||
|
||||
|
||||
private static String readString() {
|
||||
if (!scanner.hasNextLine()) Console.stopAppWithSaving();
|
||||
var input = scanner.nextLine().trim();
|
||||
return input.isEmpty() ? null : input;
|
||||
}
|
||||
|
||||
private static Long readLong() {
|
||||
var input = scanner.nextLine().trim();
|
||||
var input = readString();
|
||||
if (input == null || input.isEmpty()) return null;
|
||||
try {
|
||||
return Long.parseLong(input);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
throw new NumberFormatException(String.format("Не могу распознать %s как число", input));
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,69 +130,100 @@ public class Console {
|
||||
return new Location(locationX, locationY, locationName);
|
||||
}
|
||||
|
||||
public static ProductDTO readProductDTO() throws IllegalProductDtoException {
|
||||
try {
|
||||
var productDTO = new ProductDTO();
|
||||
System.out.println("Заполните следующие поля: ");
|
||||
System.out.print("Название: ");
|
||||
productDTO.setName(readString());
|
||||
System.out.print("Координата x: ");
|
||||
long x = readLong();
|
||||
System.out.print("Координата y: ");
|
||||
long y = readLong();
|
||||
productDTO.setCoordinates(new Coordinates(x, y));
|
||||
System.out.print("Цена: ");
|
||||
productDTO.setPrice(readLong());
|
||||
System.out.print("Номер части: ");
|
||||
productDTO.setPartNumber(readString());
|
||||
System.out.print("Себестоимость: ");
|
||||
productDTO.setManufactureCost(readLong());
|
||||
productDTO.setUnitOfMeasure(readUnitOfMeasure());
|
||||
System.out.print("Имя владельца: ");
|
||||
var ownerName = readString();
|
||||
System.out.print("Данные паспорта: ");
|
||||
var passportId = readString();
|
||||
Color color = readColor();
|
||||
productDTO.setOwner(new Person(ownerName, passportId, color, readLocation()));
|
||||
return productDTO;
|
||||
} catch (ColorFormatException | UnitOfMeasureFormatException e) {
|
||||
throw new IllegalProductDtoException(e.getMessage());
|
||||
} catch (NumberFormatException | NullPointerException | IllegalFormatConversionException e) {
|
||||
throw new IllegalProductDtoException();
|
||||
private static String readPartNumber() throws IllegalArgumentException {
|
||||
var partNumber = readString();
|
||||
var partNumbers = productCollection.toList()
|
||||
.stream()
|
||||
.map(Product::getPartNumber)
|
||||
.filter(Objects::nonNull)
|
||||
.filter(s -> s.equals(partNumber))
|
||||
.toList();
|
||||
if (!partNumbers.isEmpty()) {
|
||||
throw new IllegalArgumentException("Поле partNumber должно быть уникальным");
|
||||
}
|
||||
return partNumber;
|
||||
}
|
||||
|
||||
private static void readField(String fieldName, Runnable function) {
|
||||
while (isRunning) {
|
||||
if (fieldName != null) System.out.format("%s: ", fieldName);
|
||||
try {
|
||||
function.run();
|
||||
return;
|
||||
} catch (NullPointerException e) {
|
||||
log("Произошла ошибка при заполнении поля\nПоле не может быть пустым");
|
||||
} catch (Exception e) {
|
||||
System.out.format("Произошла ошибка при заполнении поля%n%s%n", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void readField(Runnable function) {
|
||||
readField(null, function);
|
||||
}
|
||||
|
||||
public static ProductDTO readProductDTO() {
|
||||
var productDTO = new ProductDTO();
|
||||
var coordinates = new Coordinates();
|
||||
var owner = new Person();
|
||||
log("Заполните следующие поля: ");
|
||||
|
||||
readField("Название [не может быть пустым]", () -> productDTO.setName(readString()));
|
||||
readField("Координата x [должна быть меньше 884]", () -> coordinates.setX(readLong()));
|
||||
readField("Координата y [должна быть больше -427, не может быть пустой]", () -> coordinates.setY(readLong()));
|
||||
readField("Цена [должна быть больше 0, не может быть пустой]", () -> productDTO.setPrice(readLong()));
|
||||
readField("Номер части [не больше 82 символов, должен быть уникальным]", () -> productDTO.setPartNumber(readPartNumber()));
|
||||
readField("Себестоимость [не может быть пустым]", () -> productDTO.setManufactureCost(readLong()));
|
||||
readField(() -> productDTO.setUnitOfMeasure(readUnitOfMeasure()));
|
||||
readField("Имя владельца [не может быть пустым]", () -> owner.setName(readString()));
|
||||
readField("Данные паспорта [длина строки от 5 до 22, не может быть пустым]", () -> owner.setPassportID(readString()));
|
||||
readField(() -> owner.setHairColor(readColor()));
|
||||
readField(() -> owner.setLocation(readLocation()));
|
||||
|
||||
productDTO.setCoordinates(coordinates);
|
||||
productDTO.setOwner(owner);
|
||||
return productDTO;
|
||||
}
|
||||
|
||||
public static void run() throws IOException, ValidationException {
|
||||
var productList = DbController.loadDb();
|
||||
var validationResult = Validators.validateProductList(productList);
|
||||
|
||||
if (!validationResult.stream().allMatch(ValidateResult::isValid)) {
|
||||
throw new ValidationException(String.join("\n", validationResult
|
||||
.stream()
|
||||
.map(ValidateResult::getMessage)
|
||||
.toList()));
|
||||
}
|
||||
var productCollection = new ProductCollection(productList);
|
||||
System.out.println("Добро пожаловать! Для просмотра команд введите help");
|
||||
|
||||
if (productList.isEmpty()) log("Коллекция путая\n");
|
||||
|
||||
productCollection = new ProductCollection(productList);
|
||||
log("Добро пожаловать! Для просмотра команд введите help");
|
||||
|
||||
while(isRunning) {
|
||||
System.out.print(":");
|
||||
|
||||
try {
|
||||
var input = scanner.nextLine().trim();
|
||||
var command = CommandManager.getCommandByInput(input);
|
||||
|
||||
if (command.isPresent()) {
|
||||
appendHistory(input);
|
||||
|
||||
try {
|
||||
System.out.println(command.get().action(productCollection));
|
||||
log(command.get().action(productCollection));
|
||||
} catch (CommandActionException e) {
|
||||
System.out.println(e.getMessage());
|
||||
log(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
System.out.println("Такой команды не существует. Напишите help, чтобы посмотреть список доступных команд.");
|
||||
log("Такой команды не существует. Напишите help, чтобы посмотреть список доступных команд.");
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
stopApp();
|
||||
stopAppWithoutSaving();
|
||||
}
|
||||
System.out.println();
|
||||
log();
|
||||
}
|
||||
System.out.println("Всего хорошего!");
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,6 @@ package me.zinch.console;
|
||||
*/
|
||||
public class ShutdownHook extends Thread {
|
||||
public ShutdownHook() {
|
||||
super(new Thread(Console::stopApp));
|
||||
super(new Thread(Console::stopAppWithSaving));
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,4 +9,8 @@ public class DbInitializationException extends IOException {
|
||||
public DbInitializationException() {
|
||||
super("Ошибка! Не удалось инициализировать БД. Проверьте, что структура БД верна.");
|
||||
}
|
||||
|
||||
public DbInitializationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,18 @@
|
||||
package me.zinch.files;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.DatabindException;
|
||||
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.exceptions.DbInitializationException;
|
||||
import me.zinch.models.Product;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -53,15 +51,12 @@ public class DbController {
|
||||
public static List<Product> loadDb() throws IOException {
|
||||
try {
|
||||
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path));
|
||||
Products collection = xmlMapper.readValue(bufferedInputStream, Products.class);
|
||||
List<Product> collection = xmlMapper.readValue(bufferedInputStream, Products.class).getProducts();
|
||||
bufferedInputStream.close();
|
||||
return collection.getProducts();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new FileNotFoundException("Не удалось найти файл " + path);
|
||||
} catch (DatabindException e) {
|
||||
throw new DbInitializationException();
|
||||
if (collection == null) return new ArrayList<>();
|
||||
return collection;
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Произошла неожиданная ошибка во время работы с файлом!");
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,10 +65,8 @@ public class DbController {
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(path));
|
||||
xmlMapper.writeValue(outputStreamWriter, new Products(list));
|
||||
outputStreamWriter.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new FileNotFoundException(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
throw new IOException(e);
|
||||
throw new IOException("Не удалось записать в файл");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import me.zinch.exceptions.ValidationException;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -15,7 +16,7 @@ public class Coordinates {
|
||||
@JsonProperty("x")
|
||||
private long x; //Максимальное значение поля: 883
|
||||
|
||||
@NotNull(message = "Coordinates: Поле не может быть null")
|
||||
@NotNull(message = "Coordinates: Поле y не может быть null")
|
||||
@Min(value = -427, message = "Значение координаты y должно быть больше -427")
|
||||
@JsonProperty("y")
|
||||
private Long y; //Значение поля должно быть больше -427, Поле не может быть null
|
||||
@ -27,6 +28,17 @@ public class Coordinates {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void setX(long x) {
|
||||
if (x > 883) throw new ValidationException("Максимальное значение координаты x: 883");
|
||||
this.x = x;
|
||||
}
|
||||
|
||||
public void setY(Long y) {
|
||||
if (y == null) throw new ValidationException("Поле y не может быть null");
|
||||
if (y < -427) throw new ValidationException("Значение координаты y должно быть больше -427");
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Coordinates{" +
|
||||
|
||||
@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import me.zinch.exceptions.ValidationException;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@ -36,10 +37,13 @@ public class Person {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (name == null || name.isEmpty()) throw new ValidationException("Имя персоны не может быть пустым или null");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setPassportID(String passportID) {
|
||||
if (passportID == null) throw new ValidationException("Поле passportID не может быть null");
|
||||
if (passportID.length() < 5 || passportID.length() > 22) throw new ValidationException("Длина строки passportID должна быть не меньше 5 и не должна быть больше 22");
|
||||
this.passportID = passportID;
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import me.zinch.exceptions.ValidationException;
|
||||
import me.zinch.validator.NotEmpty;
|
||||
|
||||
|
||||
@ -24,7 +25,7 @@ public class ProductDTO {
|
||||
private Coordinates coordinates; //Поле не может быть null
|
||||
|
||||
@NotNull(message = "ProductDTO: Поле price не может быть null")
|
||||
@Min(value = 1, message = "Поле price не может быть null")
|
||||
@Min(value = 1, message = "ProductDTO: Значение поля price должно быть больше 0")
|
||||
@JsonProperty("price")
|
||||
private Long price; //Поле не может быть null, Значение поля должно быть больше 0
|
||||
|
||||
@ -97,18 +98,24 @@ public class ProductDTO {
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (name == null || name.isEmpty()) throw new ValidationException("Строка name не может быть пустой или null");
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setCoordinates(Coordinates coordinates) {
|
||||
if (coordinates == null) throw new ValidationException("Поле coordinates не может быть null");
|
||||
this.coordinates = coordinates;
|
||||
}
|
||||
|
||||
public void setPrice(Long price) {
|
||||
if (price == null) throw new ValidationException("Поле price не может быть null");
|
||||
if (price <= 0) throw new ValidationException("Поле price не может быть null");
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public void setPartNumber(String partNumber) {
|
||||
if (partNumber.isEmpty()) throw new ValidationException("Строка partNumber не может быть пустой");
|
||||
if (partNumber.length() > 82) throw new ValidationException("Длина строки partNumber не должна быть больше 82");
|
||||
this.partNumber = partNumber;
|
||||
}
|
||||
|
||||
@ -117,10 +124,12 @@ public class ProductDTO {
|
||||
}
|
||||
|
||||
public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) {
|
||||
if (unitOfMeasure == null) throw new ValidationException("Поле unitOfMeasure не может быть null");
|
||||
this.unitOfMeasure = unitOfMeasure;
|
||||
}
|
||||
|
||||
public void setOwner(Person owner) {
|
||||
if (owner == null) throw new ValidationException("Поле owner не может быть null");
|
||||
this.owner = owner;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package me.zinch.validator;
|
||||
|
||||
import jakarta.validation.ConstraintViolation;
|
||||
import jakarta.validation.ValidationException;
|
||||
import me.zinch.exceptions.ValidationException;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ import me.zinch.models.Product;
|
||||
import org.hibernate.validator.HibernateValidator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -30,7 +31,7 @@ public class Validators {
|
||||
|
||||
public static List<ValidateResult<Product>> validateProductList(List<Product> db) {
|
||||
var ids = db.stream().map(Product::getId).toList();
|
||||
var partNumbers = db.stream().map(Product::getPartNumber).toList();
|
||||
var partNumbers = db.stream().map(Product::getPartNumber).filter(Objects::nonNull).toList();
|
||||
var validations = new java.util.ArrayList<>(db.stream().map(Validators::validateObject).toList());
|
||||
if (ids.size() != Set.copyOf(ids).size()) {
|
||||
validations.add(new ValidateResult<>("Поле id должно быть уникальным"));
|
||||
|
||||
@ -24,7 +24,7 @@ public class ProductCollection {
|
||||
public ProductCollection(List<Product> list) throws ValidationException {
|
||||
productList = new TreeSet<>(Comparator.comparingLong(Product::getId));
|
||||
productList.addAll(list);
|
||||
idIncrementor = productList.last().getId() + 1;
|
||||
idIncrementor = productList.isEmpty() ? 1L : productList.last().getId() + 1;
|
||||
}
|
||||
|
||||
private Long generateId() {
|
||||
@ -63,7 +63,7 @@ public class ProductCollection {
|
||||
public String getInfo() {
|
||||
var optionalInitDate = productList.stream().map(Product::getCreationDate).sorted().findFirst();
|
||||
ZonedDateTime initDate = optionalInitDate.orElse(ZonedDateTime.ofInstant(Instant.EPOCH, ZoneId.systemDefault()));
|
||||
return String.format("TreeSet%nInit Date: %s%nNumber of elements: %s", initDate, productList.size());
|
||||
return String.format("Структура: TreeSet%nДата инициализации: %s%nКоличество элементов: %s", initDate, productList.size());
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user