Compare commits

..

1 Commits

Author SHA1 Message Date
Nadezhda Naumova
7bff0acdd2 Merge branch 'develop' into 'main'
Лаб. 5

See merge request itmo_programming_2023/programming_2.12/student-3!1
2024-05-15 17:33:02 +00:00
106 changed files with 898 additions and 2274 deletions

View File

@ -1,85 +0,0 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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>Lab6-Client</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Lab6-Client</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
</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>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.23.1</version>
</dependency>
<dependency>
<groupId>me.zinch</groupId>
<artifactId>Lab6-Domain</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.7.1</version>
<configuration>
<archive>
<manifest>
<mainClass>me.zinch.Lab6.Client.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,14 +0,0 @@
package me.zinch.Lab6.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.console.ShutdownHook;
/**
* Main class for running the application.
*/
public class App {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
Console.run();
}
}

View File

@ -1,52 +0,0 @@
package me.zinch.Lab6.Client.client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.ConnectionErrorException;
import me.zinch.Lab6.Client.exceptions.ResponseException;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.Message;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class Client {
private final String address;
private final int port;
public Client(String address, int port) throws ConnectionErrorException, ResponseException {
this.address = address;
this.port = port;
try {
if (sendMessage(new BodylessMessage(MessageType.HELLO)).getType() == MessageType.HELLO) {
Console.log(String.format("Соединение с сервером %s:%s установлено", address, port));
}
} catch (IOException | ClassNotFoundException e) {
throw new ResponseException();
}
}
public Message sendMessage(Message message) throws ClassNotFoundException, IOException {
try (var socket = new Socket(address, port);
var bufferedSocketOutputStream = new BufferedOutputStream(socket.getOutputStream());
var byteArrayOutputStream = new ByteArrayOutputStream();
var objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
objectOutputStream.writeObject(message);
objectOutputStream.flush();
bufferedSocketOutputStream.write(byteArrayOutputStream.toByteArray());
bufferedSocketOutputStream.flush();
try (var objectInputStream = new ObjectInputStream(socket.getInputStream())) {
return (Message) objectInputStream.readObject();
}
} catch (IOException e) {
throw new IOException("Сервер не доступен");
}
}
}

View File

@ -1,33 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* The Add class represents a command to add a new element to a collection.
* It extends the Command class.
*/
public class Add extends Command {
public Add() {
super("add {element}", "добавить новый элемент в коллекцию", Pattern.compile("^add"));
}
@Override
public String action(Client client) throws CommandActionException {
try {
var productDTO = Console.readProductDTO();
var request = new BodyfulMessage(MessageType.POST, Console.getLastCommand(), productDTO);
var response = (BodylessMessage) client.sendMessage(request);
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,32 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* The AddIfMax class represents a command to add a new element to a collection if its price value exceeds the maximum price value in the collection.
* It extends the Command class.
*/
public class AddIfMax extends Command {
public AddIfMax() {
super("add_if_max {element}", "добавить новый элемент в коллекцию, если его значение цены превышает значение наибольшей цены этой коллекции", Pattern.compile("^add_if_max"));
}
@Override
public String action(Client client) throws CommandActionException {
try {
var productDTO = Console.readProductDTO();
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), productDTO));
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,32 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* The AddIfMin class represents a command to add a new element to a collection if its price is lower than the lowest price in the collection.
* It extends the Command class.
*/
public class AddIfMin extends Command {
public AddIfMin() {
super("add_if_min {element}", "добавить новый элемент в коллекцию, если его значение цены меньше, чем у наименьшей цены этой коллекции", Pattern.compile("^add_if_min"));
}
@Override
public String action(Client client) {
try {
var productDTO = Console.readProductDTO();
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), productDTO));
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,11 +0,0 @@
package me.zinch.Lab6.Client.commands;
/**
* The Clear class represents a command to clear a collection.
* It extends the Command class.
*/
public class Clear extends Command {
public Clear() {
super("clear", "очистить коллекцию");
}
}

View File

@ -1,59 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* Represents a command that can be executed.
*/
public abstract class Command {
private final String name;
private final String description;
private final Pattern pattern;
public Command(String name, String description, Pattern pattern) {
this.name = name;
this.description = description;
this.pattern = pattern;
}
public Command(String name, String description) {
this.name = name;
this.description = description;
this.pattern = Pattern.compile("^" + name);
}
public final String getName() {
return name;
}
public final String getSignature() {
return name.split(" ")[0];
}
public final String getDescription() {
return description;
}
public final Pattern getPattern() {
return pattern;
}
public final boolean checkPattern(String input) {
return pattern.matcher(input).matches();
}
public String action(Client client) throws CommandActionException {
try {
var response = (BodylessMessage) client.sendMessage(new BodylessMessage(MessageType.GET, this.getName()));
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,31 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* A command to filter elements whose 'name' field contains the specified substring.
*/
public class FilterContainsName extends Command {
public FilterContainsName() {
super("filter_contains_name name", "вывести элементы, значение поля name которых содержит заданную подстроку", Pattern.compile("^filter_contains_name \\w+"));
}
@Override
public String action(Client client) throws CommandActionException {
try {
String name = Console.getLastCommand().split(" ")[1];
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), name));
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,29 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
public class GetProductById extends Command {
public GetProductById() {
super("get_product_by_id {element}", "", Pattern.compile("^get_product_by_id +."));
}
@Override
public String action(Client client) throws CommandActionException {
try {
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), id));
if (response.getType() == MessageType.ERROR) throw new CommandActionException(response.getBody().toString());
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,10 +0,0 @@
package me.zinch.Lab6.Client.commands;
/**
* A command to display information about the collection (type, initialization date, number of elements, etc.) to the standard output stream.
*/
public class Info extends Command {
public Info() {
super("info", "вывести в стандартный поток вывода информацию о коллекции (тип, дата инициализации, количество элементов и т.д.)");
}
}

View File

@ -1,29 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
public class IsIdExists extends Command {
public IsIdExists() {
super("is_id_exists {element}", "", Pattern.compile("^is_id_exists +."));
}
@Override
public String action(Client client) throws CommandActionException {
try {
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), id));
if (response.getType() == MessageType.ERROR) throw new CommandActionException(response.getBody().toString());
return response.getBody().toString();
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,33 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* A command to remove an element from the collection by its id.
*/
public class Remove extends Command {
public Remove() {
super("remove_by_id id", "удалить элемент из коллекции по его id", Pattern.compile("^remove_by_id .+"));
}
@Override
public String action(Client client) throws CommandActionException {
try {
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), id));
return response.getBody().toString();
} catch (NumberFormatException e) {
return "Неверный аргумент, id может быть только число";
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,33 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* A command to remove all elements from the collection that have ids lower than the specified id.
*/
public class RemoveLower extends Command {
public RemoveLower() {
super("remove_lower id", "удалить из коллекции все элементы, меньшие, чем заданный по id", Pattern.compile("^remove_lower .+"));
}
@Override
public String action(Client client) {
try {
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
var response = (BodylessMessage) client.sendMessage(new BodyfulMessage(MessageType.POST, Console.getLastCommand(), id));
return response.getBody().toString();
} catch (NumberFormatException e) {
return "Неверный аргумент, id может быть только число";
} catch (IOException | ClassNotFoundException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,10 +0,0 @@
package me.zinch.Lab6.Client.commands;
/**
* A command to save the collection to a file.
*/
public class Save extends Command {
public Save() {
super("save", "save the collection to a file");
}
}

View File

@ -1,48 +0,0 @@
package me.zinch.Lab6.Client.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Domain.dto.BodyfulMessage;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.MessageBody;
import me.zinch.Lab6.Domain.dto.MessageType;
import java.io.IOException;
import java.util.regex.Pattern;
/**
* A command to update the value of an element in the collection, whose id is specified.
*/
public class Update extends Command {
public Update() {
super("update id {element}", "обновить значение элемента коллекции, id которого равен заданному", Pattern.compile("^update .+"));
}
@Override
public String action(Client client) throws CommandActionException {
try {
var command = Console.getLastCommand();
Long id = Long.parseLong(command.split(" ")[1]);
var isIdExists = new IsIdExists();
Console.appendHistory(String.format("%s %s", isIdExists.getSignature(), id));
isIdExists.action(client);
var getProductById = new GetProductById();
Console.appendHistory(String.format("%s %s", getProductById.getSignature(), id));
var product = getProductById.action(client);
Console.log("Вы собираетесь обновить следующий продукт");
Console.log(product);
var productDTO = Console.readProductDTO();
var response = (BodylessMessage) client.sendMessage(
new BodyfulMessage(MessageType.POST, command,
new MessageBody(String.format("%s %s", command, id), productDTO)));
return response.getBody().toString();
} catch (IOException | ClassNotFoundException | NumberFormatException e) {
throw new CommandActionException(e);
}
}
}

View File

@ -1,208 +0,0 @@
package me.zinch.Lab6.Client.console;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.commands.CommandManager;
import me.zinch.Lab6.Client.exceptions.ColorFormatException;
import me.zinch.Lab6.Client.exceptions.CommandActionException;
import me.zinch.Lab6.Client.exceptions.ConnectionErrorException;
import me.zinch.Lab6.Client.exceptions.ResponseException;
import me.zinch.Lab6.Client.exceptions.UnitOfMeasureFormatException;
import me.zinch.Lab6.Domain.models.Color;
import me.zinch.Lab6.Domain.models.Coordinates;
import me.zinch.Lab6.Domain.models.Location;
import me.zinch.Lab6.Domain.models.Person;
import me.zinch.Lab6.Domain.models.ProductDTO;
import me.zinch.Lab6.Domain.models.UnitOfMeasure;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* Console class provides methods for interacting with the console, reading user input, and managing application flow.
* This class includes methods for appending input history, stopping the application, reading product DTO from the console, and running the application loop.
*/
public class Console {
private static final Scanner scanner = new Scanner(System.in);
private static final List<String> history = new ArrayList<>();
private static boolean isRunning = true;
private static boolean isExitMessageShowed = false;
private static Client client;
public static void appendHistory(String input) {
history.add(input);
}
public static void log(Object msg) {
if (isRunning) System.out.println(msg.toString());
}
public static void log() {
log("");
}
public static void stopAppWithoutSaving() {
showExitMessage();
isRunning = false;
}
public static void stopAppWithSaving() {
stopAppWithoutSaving();
}
public static void showExitMessage() {
if (!isExitMessageShowed) log("\nВсего хорошего!");
isExitMessageShowed = true;
}
public static String getLastCommand() {
return history.get(history.size() - 1);
}
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 = readString();
if (input == null || input.isEmpty()) return null;
try {
return Long.parseLong(input);
} catch (NumberFormatException e) {
throw new NumberFormatException(String.format("Не могу распознать %s как число", input));
}
}
private static Color readColor() throws ColorFormatException {
System.out.format("Цвет волос(%s): ", Color.getColorsString());
var input = readString();
if (input == null || input.isEmpty()) return null;
try {
return Color.create(Integer.parseInt(input));
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
try {
return Color.create(input);
} catch (IllegalArgumentException ex) {
throw new ColorFormatException(ex.getMessage());
}
}
}
private static UnitOfMeasure readUnitOfMeasure() throws UnitOfMeasureFormatException {
System.out.format("Единица измерения(%s): ", UnitOfMeasure.getUnitOfMeasuerString());
var input = readString();
try {
return UnitOfMeasure.create(Integer.parseInt(input));
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
try {
return UnitOfMeasure.create(input);
} catch (IllegalArgumentException ex) {
throw new ColorFormatException(ex.getMessage());
}
}
}
private static Location readLocation() throws NumberFormatException {
System.out.print("Создать поле Location? (Y/n): ");
var input = readString();
if (input != null && input.equalsIgnoreCase("n")) return null;
System.out.print("Координата x локации: ");
var locationX = Float.parseFloat(readString());
System.out.print("Координата y локации: ");
var locationY = Integer.parseInt(readString());
System.out.print("Название: ");
var locationName = readString();
return new Location(locationX, locationY, locationName);
}
private static void setField(String fieldName, Runnable function) {
while (isRunning) {
if (fieldName != null) System.out.format("%s: ", fieldName);
try {
function.run();
return;
} catch (NullPointerException e) {
log("Произошла ошибка при заполнении поля\оле не может быть пустым");
} catch (Exception e) {
System.out.format("Произошла ошибка при заполнении поля%n%s%n", e.getMessage());
}
}
}
private static void setField(Runnable function) {
setField(null, function);
}
private static String readField(String message) {
System.out.print(message + ": ");
return readString();
}
public static ProductDTO readProductDTO() {
var productDTO = new ProductDTO();
var coordinates = new Coordinates();
var owner = new Person();
log("Заполните следующие поля: ");
setField("Название [не может быть пустым]", () -> productDTO.setName(readString()));
setField("Координата x [должна быть меньше 884]", () -> coordinates.setX(readLong()));
setField("Координата y [должна быть больше -427, не может быть пустой]", () -> coordinates.setY(readLong()));
setField("Цена [должна быть больше 0, не может быть пустой]", () -> productDTO.setPrice(readLong()));
setField("Номер части [не больше 82 символов, должен быть уникальным]", () -> productDTO.setPartNumber(readString()));
setField("Себестоимость [не может быть пустым]", () -> productDTO.setManufactureCost(readLong()));
setField(() -> productDTO.setUnitOfMeasure(readUnitOfMeasure()));
setField("Имя владельца [не может быть пустым]", () -> owner.setName(readString()));
setField("Данные паспорта [длина строки от 5 до 22, не может быть пустым]", () -> owner.setPassportID(readString()));
setField(() -> owner.setHairColor(readColor()));
setField(() -> owner.setLocation(readLocation()));
productDTO.setCoordinates(coordinates);
productDTO.setOwner(owner);
return productDTO;
}
public static void run() {
while (client == null) {
try {
var address = readField("Хост");
int port = Integer.parseInt(readField("Порт"));
client = new Client(address, port);
} catch (NumberFormatException e) {
log("Порт должен быть числом от 1 до 65535");
} catch (ResponseException | ConnectionErrorException e) {
log("Ошибка при подключении\опробуйте ещё раз");
}
}
log("Добро пожаловать! Для просмотра команд введите help");
while (isRunning) {
System.out.print(":");
try {
var input = scanner.nextLine().trim();
var command = CommandManager.getCommandByInput(input);
if (command.isPresent()) {
appendHistory(input);
try {
log(command.get().action(client));
} catch (CommandActionException e) {
log(e.getMessage());
}
} else {
log("Такой команды не существует. Напишите help, чтобы посмотреть список доступных команд.");
}
} catch (NoSuchElementException e) {
stopAppWithoutSaving();
}
log();
}
scanner.close();
}
}

View File

@ -1,10 +0,0 @@
package me.zinch.Lab6.Client.console;
/**
* Represents a shutdown hook that is executed when the application is stopped.
*/
public class ShutdownHook extends Thread {
public ShutdownHook() {
super(new Thread(Console::stopAppWithSaving));
}
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Client.exceptions;
/**
* Represents an exception that occurs during the execution of a command action.
*/
public class CommandActionException extends RuntimeException {
public CommandActionException() {
super("Ошибка во время выполнения команды");
}
public CommandActionException(String message) {
super(message);
}
public CommandActionException(Throwable e) {
super(e);
}
}

View File

@ -1,9 +0,0 @@
package me.zinch.Lab6.Client.exceptions;
import java.io.IOException;
public class ConnectionErrorException extends IOException {
public ConnectionErrorException() {
super("Произошла ошибка при подключении, сервер не доступен");
}
}

View File

@ -1,16 +0,0 @@
package me.zinch.Lab6.Client.exceptions;
import java.io.IOException;
/**
* Represents an exception that occurs when the database initialization fails.
*/
public class DbInitializationException extends IOException {
public DbInitializationException() {
super("Ошибка! Не удалось инициализировать БД. Проверьте, что структура БД верна.");
}
public DbInitializationException(String message) {
super(message);
}
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Client.exceptions;
/**
* Represents an exception that occurs when an illegal product DTO is encountered during the creation or modification of a product.
*/
public class IllegalProductDtoException extends CommandActionException {
public IllegalProductDtoException() {
super("Ошибка при создании или изменении продукта. Были введены некорректные значения.");
}
public IllegalProductDtoException(String message) {
super(message);
}
public IllegalProductDtoException(Throwable e) {
super(e);
}
}

View File

@ -1,7 +0,0 @@
package me.zinch.Lab6.Client.exceptions;
public class ResponseException extends ClassNotFoundException {
public ResponseException() {
super("Ошибка при обработке данных от сервера.");
}
}

View File

@ -1,32 +0,0 @@
package me.zinch.Lab6.Client.files;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* The ScriptLoader class provides methods to load scripts from files.
*/
public class ScriptLoader {
public static List<String> loadList(String path) throws IOException {
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path));
Scanner scanner = new Scanner(bufferedInputStream);
List<String> list = new ArrayList<>();
while (scanner.hasNextLine()) {
list.add(scanner.nextLine());
}
bufferedInputStream.close();
scanner.close();
return list;
} catch (FileNotFoundException e) {
throw new FileNotFoundException(String.format("Файл %s не найден.", path));
} catch (IOException e) {
throw new IOException("Произошла неожиданная ошибка во время работы с файлом!");
}
}
}

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>Lab6-Domain</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.1.Final</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,10 +0,0 @@
package me.zinch.Lab6.Domain.dto;
import java.io.Serializable;
public class BodyfulMessage extends Message {
public BodyfulMessage(MessageType type, String command, Serializable body) {
this.setType(type);
this.setBody(new MessageBody(command, body));
}
}

View File

@ -1,12 +0,0 @@
package me.zinch.Lab6.Domain.dto;
public class BodylessMessage extends Message {
public BodylessMessage(MessageType type) {
this.setType(type);
}
public BodylessMessage(MessageType type, String command) {
this.setType(type);
this.setBody(command);
}
}

View File

@ -1,32 +0,0 @@
package me.zinch.Lab6.Domain.dto;
import java.io.Serializable;
public abstract class Message implements Serializable {
private MessageType type;
private Serializable body;
public final MessageType getType() {
return type;
}
public final void setType(MessageType type) {
this.type = type;
}
public final Object getBody() {
return body;
}
public final void setBody(Serializable body) {
this.body = body;
}
@Override
public String toString() {
return "Message{" +
"type=" + type +
", body=" + body +
'}';
}
}

View File

@ -1,29 +0,0 @@
package me.zinch.Lab6.Domain.dto;
import java.io.Serializable;
public class MessageBody implements Serializable {
private String command;
private Serializable body;
public MessageBody(String command, Serializable body) {
this.command = command;
this.body = body;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public Serializable getBody() {
return body;
}
public void setBody(Serializable body) {
this.body = body;
}
}

View File

@ -1,11 +0,0 @@
package me.zinch.Lab6.Domain.dto;
import java.io.Serializable;
public enum MessageType implements Serializable {
GET,
POST,
ERROR,
OK,
HELLO
}

View File

@ -1,31 +0,0 @@
package me.zinch.Lab6.Domain.dto;
import me.zinch.Lab6.Domain.models.ProductDTO;
import java.io.Serializable;
public class UpdateBody implements Serializable {
private Long id;
private ProductDTO productDTO;
public UpdateBody(Long id, ProductDTO productDTO) {
this.id = id;
this.productDTO = productDTO;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public ProductDTO getProductDTO() {
return productDTO;
}
public void setProductDTO(ProductDTO productDTO) {
this.productDTO = productDTO;
}
}

View File

@ -1,34 +0,0 @@
package me.zinch.Lab6.Domain.wrapper;
import me.zinch.Lab6.Domain.models.Product;
import me.zinch.Lab6.Domain.models.ProductDTO;
import java.util.List;
public interface IStorage {
public Product getProductById(Long id);
public Product addProduct(ProductDTO productDTO);
public Product updateProduct(Long id, ProductDTO productDTO);
public Product removeProduct(Long id);
public String getInfo();
public void clear();
public Long getMaxPrice();
public Long getMinPrice();
public boolean isProductIdExists(Long id);
public Integer removeLover(Long id);
public String filterContainsName(String name);
public String getUniqueManufactureCost();
public List<Product> toList();
}

View File

@ -1,106 +0,0 @@
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
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>Lab6-Server</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>Lab6-Server</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
</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>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.13</version>
</dependency>
<dependency>
<groupId>me.zinch</groupId>
<artifactId>Lab6-Domain</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>3.26.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.7.1</version>
<configuration>
<archive>
<manifest>
<mainClass>me.zinch.Lab6.Server.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,14 +0,0 @@
package me.zinch.Lab6.Server;
import me.zinch.Lab6.Server.console.Console;
import java.io.IOException;
/**
* Main class for running the application.
*/
public class App {
public static void main(String[] args) throws IOException {
Console.run();
}
}

View File

@ -1,185 +0,0 @@
package me.zinch.Lab6.Server;
import me.zinch.Lab6.Domain.dto.BodylessMessage;
import me.zinch.Lab6.Domain.dto.Message;
import me.zinch.Lab6.Domain.dto.MessageBody;
import me.zinch.Lab6.Domain.dto.MessageType;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.commands.CommandManager;
import me.zinch.Lab6.Server.commands.GetCommand;
import me.zinch.Lab6.Server.commands.PostCommand;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import me.zinch.Lab6.Server.files.DbController;
import me.zinch.Lab6.Server.wrapper.ProductCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class Server {
private static final Logger log = LoggerFactory.getLogger(Server.class);
private final Selector selector;
private final ServerSocketChannel serverSocketChannel;
private final Map<SocketAddress, Message> usersMessages = new HashMap<>();
private final IStorage storage;
private boolean isRunning = false;
public Server(int port, IStorage collection) throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(port));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
isRunning = true;
if (collection == null) collection = new ProductCollection(new ArrayList<>());
storage = collection;
log.info("The server has been assigned to port {}", port);
}
public static ByteBuffer serialize(Serializable obj) throws IOException {
try (var bOut = new ByteArrayOutputStream();
var oOut = new ObjectOutputStream(bOut)) {
oOut.writeObject(obj);
oOut.flush();
return ByteBuffer.wrap(bOut.toByteArray());
}
}
public void run() throws IOException {
run(() -> {
});
}
public void run(Runnable middleware) throws IOException {
log.info("The server is running");
while (isRunning) {
selector.select(this::handleKey);
middleware.run();
}
}
private void handleKey(SelectionKey key) {
try {
if (key.isAcceptable()) handleAccept(key);
if (key.isReadable()) handleRead(key);
if (key.isWritable()) handleWrite(key);
} catch (IOException | ClassNotFoundException e) {
log.error(e.getMessage(), e);
key.cancel();
}
}
private void handleAccept(SelectionKey key) throws IOException {
var server = (ServerSocketChannel) key.channel();
var client = server.accept();
if (client != null) {
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
log.info("Received connection from {}", client.getRemoteAddress());
}
}
private void handleRead(SelectionKey key) throws IOException, ClassNotFoundException {
var client = (SocketChannel) key.channel();
var buffer = ByteBuffer.allocate(8192);
int bytesRead = client.read(buffer);
if (bytesRead == -1) {
client.close();
} else {
buffer.flip();
var oIn = new ObjectInputStream(new ByteArrayInputStream(buffer.array()));
var msg = (Message) oIn.readObject();
log.info("Received message {} from {}", msg, client.getRemoteAddress());
usersMessages.put(client.getRemoteAddress(), msg);
client.register(selector, SelectionKey.OP_WRITE);
oIn.close();
}
}
private void handleWrite(SelectionKey key) throws IOException {
var client = (SocketChannel) key.channel();
var message = usersMessages.get(client.getRemoteAddress());
if (message == null) {
log.error("Message for {} not found", client.getRemoteAddress());
client.close();
return;
}
ByteBuffer responseBuffer = createResponse(message);
if (responseBuffer != null) {
client.write(responseBuffer);
log.info("Sent message to {}", client.getRemoteAddress());
}
client.close();
}
private ByteBuffer createResponse(Message message) throws IOException {
return switch (message.getType()) {
case HELLO -> serialize(new BodylessMessage(MessageType.HELLO));
case GET -> handleGetCommand((String) message.getBody());
case POST -> handlePostCommand((MessageBody) message.getBody());
default -> {
log.error("Message type not supported");
yield null;
}
};
}
private ByteBuffer handleGetCommand(String inputCommand) throws IOException {
var command = CommandManager.getCommandByInput(inputCommand);
if (command.isEmpty()) {
log.error("Command {} not found", inputCommand);
return serialize(new BodylessMessage(MessageType.ERROR, String.format("Command %s not found", inputCommand)));
}
try {
var action = (GetCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(storage)));
} catch (CommandActionException e) {
return serialize(new BodylessMessage(MessageType.ERROR, e.getMessage()));
}
}
private ByteBuffer handlePostCommand(MessageBody request) throws IOException {
var inputCommand = request.getCommand();
var body = request.getBody();
var command = CommandManager.getCommandByInput(inputCommand);
if (command.isEmpty()) {
log.error("Command {} not found", inputCommand);
return serialize(new BodylessMessage(MessageType.ERROR, String.format("Command %s not found", inputCommand)));
}
try {
var action = (PostCommand) command.get();
return serialize(new BodylessMessage(MessageType.OK, action.action(storage, body)));
} catch (CommandActionException e) {
return serialize(new BodylessMessage(MessageType.ERROR, e.getMessage()));
}
}
public void stop() throws IOException {
isRunning = false;
serverSocketChannel.close();
selector.close();
DbController.saveDb(storage.toList());
}
}

View File

@ -1,37 +0,0 @@
package me.zinch.Lab6.Server;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.configs.ServerConfig;
import me.zinch.Lab6.Server.wrapper.ProductCollection;
import java.io.IOException;
import java.util.ArrayList;
public class ServerBuilder {
private ServerConfig serverConfig = new ServerConfig();
public ServerBuilder() {
serverConfig.setPort(3000);
serverConfig.setCollection(new ProductCollection(new ArrayList<>()));
}
public ServerBuilder(ServerConfig serverConfig) {
this.serverConfig = serverConfig;
}
public ServerBuilder setPort(int port) {
if (port < 1 || port > 65535) throw new IllegalArgumentException("Port must be between 1 and 65535");
serverConfig.setPort(port);
return new ServerBuilder(serverConfig);
}
public ServerBuilder setCollection(IStorage collection) {
serverConfig.setCollection(collection);
return new ServerBuilder(serverConfig);
}
public Server build() throws IOException {
return new Server(serverConfig.getPort(),
serverConfig.getCollection());
}
}

View File

@ -1,31 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.exceptions.ValidationException;
import me.zinch.Lab6.Domain.models.ProductDTO;
import me.zinch.Lab6.Domain.validator.Validators;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
/**
* The Add class represents a command to add a new element to a collection.
* It extends the Command class.
*/
public class Add extends PostCommand {
public Add() {
super("add {element}", "добавить новый элемент в коллекцию", Pattern.compile("^add"));
}
@Override
public String action(IStorage productCollection, Object object) throws CommandActionException {
var productDTO = (ProductDTO) object;
try {
Validators.validateObject(productDTO).throwIfNotValid();
var product = productCollection.addProduct(productDTO);
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
} catch (ValidationException e) {
throw new CommandActionException(String.format("Произошла ошибка при добавлении%n%s", e.getMessage()));
}
}
}

View File

@ -1,38 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.exceptions.ValidationException;
import me.zinch.Lab6.Domain.models.ProductDTO;
import me.zinch.Lab6.Domain.validator.Validators;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
/**
* The AddIfMax class represents a command to add a new element to a collection if its price value exceeds the maximum price value in the collection.
* It extends the Command class.
*/
public class AddIfMax extends PostCommand {
public AddIfMax() {
super("add_if_max {element}", "добавить новый элемент в коллекцию, если его значение цены превышает значение наибольшей цены этой коллекции", Pattern.compile("^add_if_max .+"));
}
@Override
public String action(IStorage productCollection, Object object) throws CommandActionException {
try {
var productDTO = (ProductDTO) object;
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());
}
} catch (NumberFormatException e) {
return "Неверный аргумент, id может быть только число";
}
}
}

View File

@ -1,38 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.exceptions.ValidationException;
import me.zinch.Lab6.Domain.models.ProductDTO;
import me.zinch.Lab6.Domain.validator.Validators;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
/**
* The AddIfMin class represents a command to add a new element to a collection if its price is lower than the lowest price in the collection.
* It extends the Command class.
*/
public class AddIfMin extends PostCommand {
public AddIfMin() {
super("add_if_min {element}", "добавить новый элемент в коллекцию, если его значение цены меньше, чем у наименьшей цены этой коллекции", Pattern.compile("^add_if_min .+"));
}
@Override
public String action(IStorage productCollection, Object object) {
try {
var productDTO = (ProductDTO) object;
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());
}
} catch (NumberFormatException e) {
return "Неверный аргумент, id может быть только число";
}
}
}

View File

@ -1,48 +0,0 @@
package me.zinch.Lab6.Server.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Manages the registration and retrieval of commands.
*/
public class CommandManager {
private static final List<Command> commandList = new ArrayList<>();
static {
registerCommand(new Show());
registerCommand(new Exit());
registerCommand(new Info());
registerCommand(new Clear());
registerCommand(new PrintAscending());
registerCommand(new PrintUniqueManufactureCost());
registerCommand(new FilterContainsName());
registerCommand(new Add());
registerCommand(new IsIdExists());
registerCommand(new Update());
registerCommand(new Remove());
registerCommand(new AddIfMax());
registerCommand(new AddIfMin());
registerCommand(new RemoveLower());
registerCommand(new Save());
registerCommand(new GetProduct());
registerCommand(new Help());
}
public static void registerCommand(Command command) {
commandList.add(command);
}
public static String getRegisteredCommand() {
return String.join("\n", commandList
.stream()
.map(command -> String.format("%s - %s", command.getName(), command.getDescription()))
.toList());
}
public static Optional<Command> getCommandByInput(String input) {
return commandList.stream().filter(command -> command.checkPattern(input)).findFirst();
}
}

View File

@ -1,19 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.console.Console;
/**
* A command to exit the program without saving to a file.
*/
public class Exit extends GetCommand {
public Exit() {
super("exit", "terminate the program (without saving to a file)");
}
@Override
public String action(IStorage productCollection) {
Console.stopApp();
return "";
}
}

View File

@ -1,21 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import java.util.regex.Pattern;
/**
* A command to filter elements whose 'name' field contains the specified substring.
*/
public class FilterContainsName extends PostCommand {
public FilterContainsName() {
super("filter_contains_name name", "вывести элементы, значение поля name которых содержит заданную подстроку", Pattern.compile("^filter_contains_name .+"));
}
@Override
public String action(IStorage productCollection, Object object) {
String name = object.toString();
var result = productCollection.filterContainsName(name);
return result.isEmpty() ? "Таких элементов не найдено" : result;
}
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
public abstract class GetCommand extends Command {
public GetCommand(String name, String description, Pattern pattern) {
super(name, description, pattern);
}
public GetCommand(String name, String description) {
super(name, description);
}
public abstract String action(IStorage productCollection) throws CommandActionException;
}

View File

@ -1,19 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
public class GetProduct extends PostCommand {
public GetProduct() {
super("get_product_by_id", "", Pattern.compile("^get_product_by_id +."));
}
@Override
public String action(IStorage productCollection, Object obj) throws CommandActionException {
if (!productCollection.isProductIdExists((Long) obj))
throw new CommandActionException("Продукт с таким ID не существует");
return productCollection.getProductById((Long) obj).toString();
}
}

View File

@ -1,17 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
/**
* A command to display help for available commands.
*/
public class Help extends GetCommand {
public Help() {
super("help", "display help for available commands");
}
@Override
public String action(IStorage productCollection) {
return SystemCommandManager.getRegisteredCommand();
}
}

View File

@ -1,19 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
public class IsIdExists extends PostCommand {
public IsIdExists() {
super("is_id_exists", "", Pattern.compile("^is_id_exists +."));
}
@Override
public String action(IStorage productCollection, Object obj) throws CommandActionException {
if (!productCollection.isProductIdExists((Long) obj))
throw new CommandActionException("Продукт с таким ID не существует");
return "Exists";
}
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
public abstract class PostCommand extends Command {
public PostCommand(String name, String description, Pattern pattern) {
super(name, description, pattern);
}
public PostCommand(String name, String description) {
super(name, description);
}
public abstract String action(IStorage productCollection, Object obj) throws CommandActionException;
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
/**
* A command to print the elements of the collection in ascending order.
*/
public class PrintAscending extends GetCommand {
public PrintAscending() {
super("print_ascending", "вывести элементы коллекции в порядке возрастания");
}
@Override
public String action(IStorage productCollection) {
var result = productCollection.toString();
return result.isEmpty() ? "Коллекция пуста" : result;
}
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
/**
* A command to print the unique values of the 'manufactureCost' field of all elements in the collection.
*/
public class PrintUniqueManufactureCost extends GetCommand {
public PrintUniqueManufactureCost() {
super("print_unique_manufacture_cost", "вывести уникальные значения поля manufactureCost всех элементов в коллекции");
}
@Override
public String action(IStorage productCollection) {
var result = productCollection.getUniqueManufactureCost();
return result.isEmpty() ? "Коллекция пуста" : result;
}
}

View File

@ -1,29 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
/**
* A command to remove an element from the collection by its id.
*/
public class Remove extends PostCommand {
public Remove() {
super("remove_by_id id", "удалить элемент из коллекции по его id", Pattern.compile("^remove_by_id .+"));
}
@Override
public String action(IStorage productCollection, Object object) throws CommandActionException {
try {
Long id = (Long) object;
if (productCollection.isProductIdExists(id)) {
var product = productCollection.removeProduct(id);
return String.format("Продукт %s был удалён", product.getName());
}
return "Продукта с таким id не существует";
} catch (NumberFormatException e) {
return "Неверный аргумент, id может быть только число";
}
}
}

View File

@ -1,25 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import java.util.regex.Pattern;
/**
* A command to remove all elements from the collection that have ids lower than the specified id.
*/
public class RemoveLower extends PostCommand {
public RemoveLower() {
super("remove_lower id", "удалить из коллекции все элементы, меньшие, чем заданный по id", Pattern.compile("^remove_lower .+"));
}
@Override
public String action(IStorage productCollection, Object object) {
try {
Long id = (Long) object;
var size = productCollection.removeLover(id);
return String.format("Было удалено %d продуктов", size);
} catch (NumberFormatException e) {
return "Неверный аргумент, id может быть только число";
}
}
}

View File

@ -1,18 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
/**
* A command to display all elements of the collection in string representation to the standard output stream.
*/
public class Show extends GetCommand {
public Show() {
super("show", "вывести в стандартный поток вывода все элементы коллекции в строковом представлении");
}
@Override
public String action(IStorage productCollection) {
var result = productCollection.toString();
return result.isEmpty() ? "Коллекция пуста" : result;
}
}

View File

@ -1,34 +0,0 @@
package me.zinch.Lab6.Server.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Manages the registration and retrieval of commands.
*/
public class SystemCommandManager {
private static final List<Command> commandList = new ArrayList<>();
static {
registerCommand(new Exit());
registerCommand(new Save());
registerCommand(new Help());
}
public static void registerCommand(Command command) {
commandList.add(command);
}
public static String getRegisteredCommand() {
return String.join("\n", commandList
.stream()
.map(command -> String.format("%s - %s", command.getName(), command.getDescription()))
.toList());
}
public static Optional<Command> getCommandByInput(String input) {
return commandList.stream().filter(command -> command.checkPattern(input)).findFirst();
}
}

View File

@ -1,17 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
/**
* Represents an unknown command.
*/
public class UnknownCommand extends GetCommand {
public UnknownCommand() {
super("", "");
}
@Override
public String action(IStorage productCollection) {
return "Команда не распознана.";
}
}

View File

@ -1,40 +0,0 @@
package me.zinch.Lab6.Server.commands;
import me.zinch.Lab6.Domain.dto.MessageBody;
import me.zinch.Lab6.Domain.exceptions.ValidationException;
import me.zinch.Lab6.Domain.models.ProductDTO;
import me.zinch.Lab6.Domain.validator.Validators;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import java.util.regex.Pattern;
/**
* A command to update the value of an element in the collection, whose id is specified.
*/
public class Update extends PostCommand {
public Update() {
super("update id {element}", "обновить значение элемента коллекции, id которого равен заданному", Pattern.compile("^update .+"));
}
@Override
public String action(IStorage productCollection, Object object) throws CommandActionException {
try {
var body = (MessageBody) object;
var id = Long.parseLong(body.getCommand().split(" ")[1]);
if (productCollection.isProductIdExists(id)) {
var productDTO = (ProductDTO) body.getBody();
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 может быть только число";
}
}
}

View File

@ -1,24 +0,0 @@
package me.zinch.Lab6.Server.configs;
import me.zinch.Lab6.Domain.wrapper.IStorage;
public class ServerConfig {
private Integer port;
private IStorage collection;
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
public IStorage getCollection() {
return collection;
}
public void setCollection(IStorage collection) {
this.collection = collection;
}
}

View File

@ -1,86 +0,0 @@
package me.zinch.Lab6.Server.console;
import me.zinch.Lab6.Domain.exceptions.ValidationException;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.ServerBuilder;
import me.zinch.Lab6.Server.commands.GetCommand;
import me.zinch.Lab6.Server.commands.SystemCommandManager;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import me.zinch.Lab6.Server.files.DbController;
import me.zinch.Lab6.Server.wrapper.ProductCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* Console class provides methods for interacting with the console, reading user input, and managing application flow.
* This class includes methods for appending input history, stopping the application, reading product DTO from the console, and running the application loop.
*/
public class Console {
private static final Scanner scanner = new Scanner(System.in);
private static final Logger log = LoggerFactory.getLogger(Console.class);
private static boolean isRunning = true;
private static boolean isExitMessageShowed = false;
private static IStorage collection;
public static void stopApp() {
showExitMessage();
isRunning = false;
System.exit(0);
}
public static void showExitMessage() {
if (!isExitMessageShowed) log.info("Have a nice day!");
isExitMessageShowed = true;
}
public static void run() throws IOException, ValidationException {
collection = new ProductCollection(DbController.loadDb());
var server = new ServerBuilder()
.setPort(3000)
.setCollection(collection)
.build();
Console.handleInput();
server.run();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
Console.stopApp();
try {
server.stop();
} catch (IOException e) {
throw new RuntimeException(e);
}
}));
}
public static void handleInput() {
new Thread(() -> {
while (isRunning) {
try {
var input = scanner.nextLine().trim();
var command = SystemCommandManager.getCommandByInput(input);
if (command.isPresent()) {
var action = (GetCommand) command.get();
try {
log.info(action.action(collection));
} catch (CommandActionException e) {
log.info(e.getMessage());
}
} else {
log.info("This command does not exist. Write help to see a list of available commands.");
}
} catch (NoSuchElementException e) {
stopApp();
}
}
scanner.close();
}).start();
}
}

View File

@ -1,11 +0,0 @@
package me.zinch.Lab6.Server.exceptions;
public class ColorFormatException extends IllegalArgumentException {
public ColorFormatException() {
super();
}
public ColorFormatException(String message) {
super(message);
}
}

View File

@ -1,11 +0,0 @@
package me.zinch.Lab6.Server.exceptions;
public class UnitOfMeasureFormatException extends IllegalArgumentException {
public UnitOfMeasureFormatException() {
super();
}
public UnitOfMeasureFormatException(String message) {
super(message);
}
}

View File

@ -1,11 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

View File

@ -1,8 +1,99 @@
# Лабораторная работа №6
# Лабораторная работа №5
## Выполнил
> ФИ: Зинченко Иван
>
> ИСУ: 408657
>
> Вариант: 21290
> Вариант: 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
}
```

2
db.xml
View File

@ -1 +1 @@
<Products><Product><id>6</id><name>1</name><coordinates><x>1</x><y>11</y></coordinates><creationDate>1716766945.584662600</creationDate><price>1</price><partNumber>1</partNumber><manufactureCost>231</manufactureCost><unitOfMeasure>CENTIMETERS</unitOfMeasure><owner><name>1</name><passportID>313412</passportID><hairColor>BLACK</hairColor><location><x>1.0</x><y>1</y><name>1</name></location></owner></Product><Product><id>1</id><name>Product 1</name><coordinates><x>100</x><y>50</y></coordinates><creationDate>1709294400.000000000</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>1709294700.000000000</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>1709295000.000000000</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>1709295300.000000000</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>1709295600.000000000</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>
<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>

117
db.xml.example 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>

74
pom.xml Normal file
View File

@ -0,0 +1,74 @@
<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>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
</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>
<artifactId>maven-assembly-plugin</artifactId>
<groupId>org.apache.maven.plugins</groupId>
<version>3.7.1</version>
<configuration>
<archive>
<manifest>
<mainClass>me.zinch.App</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.3</version>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,25 @@
package me.zinch;
import me.zinch.console.Console;
import me.zinch.console.ShutdownHook;
import me.zinch.exceptions.ValidationException;
import java.io.IOException;
/**
* Main class for running the application.
*/
public class App {
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new ShutdownHook());
try {
Console.run();
} catch (ValidationException e) {
System.out.println("Ошибка при валидации БД. Некоторые данные не валидны.");
System.out.println(e.getMessage());
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}

View File

@ -0,0 +1,31 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.exceptions.CommandActionException;
import me.zinch.exceptions.ValidationException;
import me.zinch.validator.Validators;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* The Add class represents a command to add a new element to a collection.
* It extends the Command class.
*/
public class Add extends Command {
public Add() {
super("add {element}", "добавить новый элемент в коллекцию", Pattern.compile("^add"));
}
@Override
public String action(ProductCollection productCollection) throws CommandActionException {
var productDTO = Console.readProductDTO();
try {
Validators.validateObject(productDTO).throwIfNotValid();
var product = productCollection.addProduct(productDTO);
return String.format("Продукт %s был добавлен под id %d", product.getName(), product.getId());
} catch (ValidationException e) {
throw new CommandActionException(e.getMessage());
}
}
}

View File

@ -0,0 +1,34 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.exceptions.CommandActionException;
import me.zinch.exceptions.ValidationException;
import me.zinch.validator.Validators;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* The AddIfMax class represents a command to add a new element to a collection if its price value exceeds the maximum price value in the collection.
* It extends the Command class.
*/
public class AddIfMax extends Command {
public AddIfMax() {
super("add_if_max {element}", "добавить новый элемент в коллекцию, если его значение цены превышает значение наибольшей цены этой коллекции", Pattern.compile("^add_if_max \\d+"));
}
@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());
}
return "Продукт не подходит под условие";
} catch (ValidationException e) {
throw new CommandActionException(e.getMessage());
}
}
}

View File

@ -0,0 +1,34 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.exceptions.CommandActionException;
import me.zinch.exceptions.ValidationException;
import me.zinch.validator.Validators;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* The AddIfMin class represents a command to add a new element to a collection if its price is lower than the lowest price in the collection.
* It extends the Command class.
*/
public class AddIfMin extends Command {
public AddIfMin() {
super("add_if_min {element}", "добавить новый элемент в коллекцию, если его значение цены меньше, чем у наименьшей цены этой коллекции", Pattern.compile("^add_if_min \\d+"));
}
@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());
}
return "Продукт не подходит под условие";
} catch (ValidationException e) {
throw new CommandActionException(e.getMessage());
}
}
}

View File

@ -1,18 +1,18 @@
package me.zinch.Lab6.Server.commands;
package me.zinch.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.wrapper.ProductCollection;
/**
* The Clear class represents a command to clear a collection.
* It extends the Command class.
*/
public class Clear extends GetCommand {
public class Clear extends Command {
public Clear() {
super("clear", "очистить коллекцию");
}
@Override
public String action(IStorage productCollection) {
public String action(ProductCollection productCollection) {
productCollection.clear();
return "Коллекция была очищена";
}

View File

@ -1,4 +1,7 @@
package me.zinch.Lab6.Server.commands;
package me.zinch.commands;
import me.zinch.exceptions.CommandActionException;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
@ -37,4 +40,6 @@ public abstract class Command {
public final boolean checkPattern(String input) {
return pattern.matcher(input).matches();
}
public abstract String action(ProductCollection productCollection) throws CommandActionException;
}

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import java.util.ArrayList;
import java.util.List;
@ -10,25 +10,6 @@ import java.util.Optional;
public class CommandManager {
private static final List<Command> commandList = new ArrayList<>();
static {
registerCommand(new Show());
registerCommand(new Exit());
registerCommand(new Info());
registerCommand(new Clear());
registerCommand(new PrintAscending());
registerCommand(new PrintUniqueManufactureCost());
registerCommand(new FilterContainsName());
registerCommand(new Add());
registerCommand(new Update());
registerCommand(new Remove());
registerCommand(new AddIfMax());
registerCommand(new AddIfMin());
registerCommand(new RemoveLower());
registerCommand(new ExecuteScript());
registerCommand(new Help());
}
public static void registerCommand(Command command) {
commandList.add(command);
}
@ -43,4 +24,24 @@ public class CommandManager {
public static Optional<Command> getCommandByInput(String input) {
return commandList.stream().filter(command -> command.checkPattern(input)).findFirst();
}
static {
registerCommand(new Show());
registerCommand(new Exit());
registerCommand(new Info());
registerCommand(new Clear());
registerCommand(new PrintAscending());
registerCommand(new PrintUniqueManufactureCost());
registerCommand(new FilterContainsName());
registerCommand(new Add());
registerCommand(new Update());
registerCommand(new Remove());
registerCommand(new AddIfMax());
registerCommand(new AddIfMin());
registerCommand(new RemoveLower());
registerCommand(new Save());
registerCommand(new ExecuteScript());
registerCommand(new Help());
}
}

View File

@ -1,8 +1,8 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.Lab6.Client.files.ScriptLoader;
import me.zinch.console.Console;
import me.zinch.files.ScriptLoader;
import me.zinch.wrapper.ProductCollection;
import java.io.IOException;
import java.util.ArrayList;
@ -15,10 +15,6 @@ import java.util.regex.Pattern;
public class ExecuteScript extends Command {
private static final List<String> executeStack = new ArrayList<>();
public ExecuteScript() {
super("execute_script file_name", "считать и исполнить скрипт из указанного файла. В скрипте содержатся команды в таком же виде, в котором их вводит пользователь в интерактивном режиме", Pattern.compile("^execute_script .+"));
}
private void addFileToStack(String filePath) {
executeStack.add(filePath);
}
@ -31,15 +27,17 @@ public class ExecuteScript extends Command {
return executeStack.contains(filePath);
}
public ExecuteScript() {
super("execute_script file_name", "считать и исполнить скрипт из указанного файла. В скрипте содержатся команды в таком же виде, в котором их вводит пользователь в интерактивном режиме", Pattern.compile("^execute_script .+"));
}
@Override
public String action(Client client) {
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) {
for (var input: commandList) {
input = input.trim();
var command = CommandManager.getCommandByInput(input);
if (command.isPresent()) {
@ -49,14 +47,13 @@ public class ExecuteScript extends Command {
if (checkFileInStack(nextFile)) continue;
addFileToStack(nextFile);
}
result.append(command.get().action(client)).append("\n");
command.get().action(productCollection);
} else {
new UnknownCommand().action(client);
new UnknownCommand().action(productCollection);
}
}
clearStack();
result.append(String.format("Скрипт %s завершил работу.", scriptFile)).append("\n");
return result.toString();
return String.format("Скрипт %s завершил работу.%n", scriptFile);
} catch (IOException e) {
return e.getMessage();
}

View File

@ -1,7 +1,7 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.Lab6.Client.console.Console;
import me.zinch.console.Console;
import me.zinch.wrapper.ProductCollection;
/**
* A command to exit the program without saving to a file.
@ -12,8 +12,8 @@ public class Exit extends Command {
}
@Override
public String action(Client client) {
Console.stopAppWithoutSaving();
public String action(ProductCollection productCollection) {
Console.stopApp();
return "";
}
}

View File

@ -0,0 +1,21 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* A command to filter elements whose 'name' field contains the specified substring.
*/
public class FilterContainsName extends Command {
public FilterContainsName() {
super("filter_contains_name name", "вывести элементы, значение поля name которых содержит заданную подстроку", Pattern.compile("^filter_contains_name \\w+"));
}
@Override
public String action(ProductCollection productCollection) {
String name = Console.getLastCommand().split(" ")[1];
return productCollection.filterContainsName(name);
}
}

View File

@ -1,6 +1,6 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.wrapper.ProductCollection;
/**
* A command to display help for available commands.
@ -11,7 +11,7 @@ public class Help extends Command {
}
@Override
public String action(Client client) {
public String action(ProductCollection productCollection) {
return CommandManager.getRegisteredCommand();
}
}

View File

@ -1,17 +1,17 @@
package me.zinch.Lab6.Server.commands;
package me.zinch.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.wrapper.ProductCollection;
/**
* A command to display information about the collection (type, initialization date, number of elements, etc.) to the standard output stream.
*/
public class Info extends GetCommand {
public class Info extends Command {
public Info() {
super("info", "вывести в стандартный поток вывода информацию о коллекции (тип, дата инициализации, количество элементов и т.д.)");
}
@Override
public String action(IStorage productCollection) {
public String action(ProductCollection productCollection) {
return productCollection.getInfo();
}
}

View File

@ -1,4 +1,6 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.wrapper.ProductCollection;
/**
* A command to print the elements of the collection in ascending order.
@ -7,4 +9,9 @@ public class PrintAscending extends Command {
public PrintAscending() {
super("print_ascending", "вывести элементы коллекции в порядке возрастания");
}
@Override
public String action(ProductCollection productCollection) {
return productCollection.toString();
}
}

View File

@ -1,4 +1,6 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.wrapper.ProductCollection;
/**
* A command to print the unique values of the 'manufactureCost' field of all elements in the collection.
@ -7,4 +9,9 @@ public class PrintUniqueManufactureCost extends Command {
public PrintUniqueManufactureCost() {
super("print_unique_manufacture_cost", "вывести уникальные значения поля manufactureCost всех элементов в коллекции");
}
@Override
public String action(ProductCollection productCollection) {
return productCollection.getUniqueManufactureCost();
}
}

View File

@ -0,0 +1,22 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* A command to remove an element from the collection by its id.
*/
public class Remove extends Command {
public Remove() {
super("remove_by_id id", "удалить элемент из коллекции по его id", Pattern.compile("^remove_by_id \\d+"));
}
@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());
}
}

View File

@ -0,0 +1,22 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* A command to remove all elements from the collection that have ids lower than the specified id.
*/
public class RemoveLower extends Command {
public RemoveLower() {
super("remove_lower id", "удалить из коллекции все элементы, меньшие, чем заданный по id", Pattern.compile("^remove_lower \\d+"));
}
@Override
public String action(ProductCollection productCollection) {
Long id = Long.parseLong(Console.getLastCommand().split(" ")[1]);
var size = productCollection.removeLover(id);
return String.format("Было удалено %d продуктов", size);
}
}

View File

@ -1,21 +1,21 @@
package me.zinch.Lab6.Server.commands;
package me.zinch.commands;
import me.zinch.Lab6.Domain.wrapper.IStorage;
import me.zinch.Lab6.Server.exceptions.CommandActionException;
import me.zinch.Lab6.Server.files.DbController;
import me.zinch.exceptions.CommandActionException;
import me.zinch.files.DbController;
import me.zinch.wrapper.ProductCollection;
import java.io.IOException;
/**
* A command to save the collection to a file.
*/
public class Save extends GetCommand {
public class Save extends Command {
public Save() {
super("save", "сохранить коллекцию в файл");
}
@Override
public String action(IStorage productCollection) throws CommandActionException {
public String action(ProductCollection productCollection) throws CommandActionException {
try {
DbController.saveDb(productCollection.toList());
return "Коллекция была сохранена";

View File

@ -1,4 +1,6 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.wrapper.ProductCollection;
/**
* A command to display all elements of the collection in string representation to the standard output stream.
@ -7,4 +9,9 @@ public class Show extends Command {
public Show() {
super("show", "вывести в стандартный поток вывода все элементы коллекции в строковом представлении");
}
@Override
public String action(ProductCollection productCollection) {
return productCollection.toString();
}
}

View File

@ -1,6 +1,6 @@
package me.zinch.Lab6.Client.commands;
package me.zinch.commands;
import me.zinch.Lab6.Client.client.Client;
import me.zinch.wrapper.ProductCollection;
/**
* Represents an unknown command.
@ -11,7 +11,7 @@ public class UnknownCommand extends Command {
}
@Override
public String action(Client client) {
public String action(ProductCollection productCollection) {
return "Команда не распознана.";
}
}

View File

@ -0,0 +1,34 @@
package me.zinch.commands;
import me.zinch.console.Console;
import me.zinch.exceptions.CommandActionException;
import me.zinch.exceptions.ValidationException;
import me.zinch.validator.Validators;
import me.zinch.wrapper.ProductCollection;
import java.util.regex.Pattern;
/**
* A command to update the value of an element in the collection, whose id is specified.
*/
public class Update extends Command {
public Update() {
super("update id {element}", "обновить значение элемента коллекции, id которого равен заданному", Pattern.compile("^update \\d+"));
}
@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());
}
}
return "Продукта с таким id не существует";
}
}

View File

@ -0,0 +1,171 @@
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;
import me.zinch.models.Color;
import me.zinch.models.Coordinates;
import me.zinch.models.Location;
import me.zinch.models.Person;
import me.zinch.models.ProductDTO;
import me.zinch.models.UnitOfMeasure;
import me.zinch.validator.ValidateResult;
import me.zinch.validator.Validators;
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.Scanner;
/**
Console class provides methods for interacting with the console, reading user input, and managing application flow.
This class includes methods for appending input history, stopping the application, reading product DTO from the console, and running the application loop.
*/
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<>();
public static void appendHistory(String input) {
history.add(input);
}
public static void stopApp() {
isRunning = false;
scanner.close();
}
public static String getLastCommand() {
return history.get(history.size() - 1);
}
private static String readString() {
var input = scanner.nextLine().trim();
return input.isEmpty() ? null : input;
}
private static Long readLong() {
var input = scanner.nextLine().trim();
try {
return Long.parseLong(input);
} catch (NumberFormatException e) {
return null;
}
}
private static Color readColor() throws ColorFormatException {
System.out.format("Цвет волос(%s): ", Color.getColorsString());
var input = readString();
if (input == null || input.isEmpty()) return null;
try {
return Color.create(Integer.parseInt(input));
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
try {
return Color.create(input);
} catch (IllegalArgumentException ex) {
throw new ColorFormatException(ex.getMessage());
}
}
}
private static UnitOfMeasure readUnitOfMeasure() throws UnitOfMeasureFormatException {
System.out.format("Единица измерения(%s): ", UnitOfMeasure.getUnitOfMeasuerString());
var input = readString();
try {
return UnitOfMeasure.create(Integer.parseInt(input));
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
try {
return UnitOfMeasure.create(input);
} catch (IllegalArgumentException ex) {
throw new ColorFormatException(ex.getMessage());
}
}
}
private static Location readLocation() throws NumberFormatException {
System.out.print("Создать поле Location? (Y/n): ");
var input = readString();
if (input != null && input.equalsIgnoreCase("n")) return null;
System.out.print("Координата x локации: ");
var locationX = Float.parseFloat(readString());
System.out.print("Координата y локации: ");
var locationY = Integer.parseInt(readString());
System.out.print("Название: ");
var locationName = readString();
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();
}
}
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");
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));
} catch (CommandActionException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Такой команды не существует. Напишите help, чтобы посмотреть список доступных команд.");
}
} catch (NoSuchElementException e) {
stopApp();
}
System.out.println();
}
System.out.println("Всего хорошего!");
}
}

View File

@ -1,10 +1,10 @@
package me.zinch.Lab6.Server.console;
package me.zinch.console;
/**
* Represents a shutdown hook that is executed when the application is stopped.
*/
public class ShutdownHook extends Thread {
public ShutdownHook() {
super();
super(new Thread(Console::stopApp));
}
}

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Client.exceptions;
package me.zinch.exceptions;
public class ColorFormatException extends IllegalArgumentException {
public ColorFormatException() {

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Server.exceptions;
package me.zinch.exceptions;
/**
* Represents an exception that occurs during the execution of a command action.

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Server.exceptions;
package me.zinch.exceptions;
import java.io.IOException;
@ -9,8 +9,4 @@ public class DbInitializationException extends IOException {
public DbInitializationException() {
super("Ошибка! Не удалось инициализировать БД. Проверьте, что структура БД верна.");
}
public DbInitializationException(String message) {
super(message);
}
}

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Server.exceptions;
package me.zinch.exceptions;
/**
* Represents an exception that occurs when an illegal product DTO is encountered during the creation or modification of a product.

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Client.exceptions;
package me.zinch.exceptions;
public class UnitOfMeasureFormatException extends IllegalArgumentException {
public UnitOfMeasureFormatException() {

View File

@ -1,11 +1,10 @@
package me.zinch.Lab6.Domain.exceptions;
package me.zinch.exceptions;
/**
* Represents an exception related to validation errors.
*/
public class ValidationException extends jakarta.validation.ValidationException {
public ValidationException() {
}
public ValidationException() {}
public ValidationException(String message) {
super(message);

View File

@ -1,18 +1,20 @@
package me.zinch.Lab6.Server.files;
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.Lab6.Domain.models.Product;
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;
@ -25,32 +27,10 @@ public class DbController {
static {
Map<String, String> env = System.getenv();
path = env.getOrDefault("DB_FILE", "db.xml");
path = env.getOrDefault("DB_FILE", "");
xmlMapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
}
public static List<Product> loadDb() {
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path));
List<Product> collection = xmlMapper.readValue(bufferedInputStream, Products.class).getProducts();
bufferedInputStream.close();
if (collection == null) return new ArrayList<>();
return collection;
} catch (IOException e) {
return new ArrayList<>();
}
}
public static void saveDb(List<Product> list) throws IOException {
try {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(path));
xmlMapper.writeValue(outputStreamWriter, new Products(list));
outputStreamWriter.close();
} catch (IOException e) {
throw new IOException("Не удалось записать в файл");
}
}
/**
* Represents a collection of products.
*/
@ -59,8 +39,7 @@ public class DbController {
@JacksonXmlProperty(localName = "Product")
private List<Product> products;
public Products() {
}
public Products() {}
public Products(List<Product> list) {
this.products = list;
@ -70,4 +49,31 @@ public class DbController {
return products;
}
}
public static List<Product> loadDb() throws IOException {
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path));
Products collection = xmlMapper.readValue(bufferedInputStream, Products.class);
bufferedInputStream.close();
return collection.getProducts();
} catch (FileNotFoundException e) {
throw new FileNotFoundException("Не удалось найти файл " + path);
} catch (DatabindException e) {
throw new DbInitializationException();
} catch (IOException e) {
throw new IOException("Произошла неожиданная ошибка во время работы с файлом!");
}
}
public static void saveDb(List<Product> list) throws IOException {
try {
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);
}
}
}

View File

@ -1,4 +1,4 @@
package me.zinch.Lab6.Server.files;
package me.zinch.files;
import java.io.BufferedInputStream;
import java.io.FileInputStream;

View File

@ -1,6 +1,5 @@
package me.zinch.Lab6.Domain.models;
package me.zinch.models;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@ -8,7 +7,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a color enumeration.
*/
public enum Color implements Serializable {
public enum Color {
GREEN("Зелёный"),
BLACK("Чёрный"),
BLUE("Синий"),
@ -21,6 +20,10 @@ public enum Color implements Serializable {
this.color = color;
}
private String getColor() {
return color;
}
public static Color create(String input) throws IllegalArgumentException {
for (var unit : List.of(Color.values())) {
if (unit.getColor().equalsIgnoreCase(input)) return unit;
@ -39,10 +42,6 @@ public enum Color implements Serializable {
.toList());
}
private String getColor() {
return color;
}
@Override
public String toString() {
return "Color{" +

View File

@ -1,54 +1,32 @@
package me.zinch.Lab6.Domain.models;
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 me.zinch.Lab6.Domain.exceptions.ValidationException;
import java.io.Serializable;
import java.util.Objects;
/**
* Represents coordinates with x and y values.
*/
public class Coordinates implements Serializable {
public class Coordinates {
@Max(value = 883, message = "Coordinates: Максимальное значение координаты x: 883")
@JsonProperty("x")
private long x; //Максимальное значение поля: 883
@NotNull(message = "Coordinates: Поле y не может быть null")
@NotNull(message = "Coordinates: Поле не может быть null")
@Min(value = -427, message = "Значение координаты y должно быть больше -427")
@JsonProperty("y")
private Long y; //Значение поля должно быть больше -427, Поле не может быть null
public Coordinates() {
}
public Coordinates() {}
public Coordinates(long x, Long y) {
this.x = x;
this.y = y;
}
public long getX() {
return x;
}
public void setX(long x) {
if (x > 883) throw new ValidationException("Максимальное значение координаты x: 883");
this.x = x;
}
public Long getY() {
return y;
}
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{" +

View File

@ -1,16 +1,15 @@
package me.zinch.Lab6.Domain.models;
package me.zinch.models;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.NotNull;
import me.zinch.Lab6.Domain.validator.NotEmpty;
import me.zinch.validator.NotEmpty;
import java.io.Serializable;
import java.util.Objects;
/**
* Represents a location with x and y coordinates and a name.
*/
public class Location implements Serializable {
public class Location {
@JsonProperty("x")
private float x;
@ -22,8 +21,7 @@ public class Location implements Serializable {
@JsonProperty("name")
private String name; //Строка не может быть пустой, Поле может быть null
public Location() {
}
public Location() {}
public Location(float x, Integer y, String name) {
this.x = x;

View File

@ -1,18 +1,16 @@
package me.zinch.Lab6.Domain.models;
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 me.zinch.Lab6.Domain.exceptions.ValidationException;
import java.io.Serializable;
import java.util.Objects;
/**
* Represents a person with a name, passport ID, hair color, and location.
*/
public class Person implements Serializable {
public class Person {
@NotBlank(message = "Person: Имя персоны не может быть пустым или null")
@JsonProperty("name")
private String name; //Поле не может быть null, Строка не может быть пустой
@ -28,8 +26,7 @@ public class Person implements Serializable {
@JsonProperty("location")
private Location location; //Поле может быть null
public Person() {
}
public Person() {}
public Person(String name, String passportID, Color hairColor, Location location) {
this.name = name;
@ -39,14 +36,10 @@ public class Person implements Serializable {
}
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;
}

View File

@ -1,20 +1,19 @@
package me.zinch.Lab6.Domain.models;
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.Lab6.Domain.validator.NotEmpty;
import me.zinch.validator.NotEmpty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.Objects;
/**
* Represents a product with various attributes such as ID, name, coordinates, creation date, price, part number, manufacture cost, unit of measure, and owner.
*/
public class Product implements Serializable {
public class Product {
@NotNull(message = "Product: Поле id не может быть null")
@Min(value = 1, message = "Product: Значение поля id должно быть больше 0")
@JsonProperty("id")
@ -53,19 +52,18 @@ public class Product implements Serializable {
@JsonProperty("owner")
private Person owner; //Поле не может быть null
public Product() {
}
public Product() {}
public Product(Long id,
String name,
Coordinates coordinates,
ZonedDateTime creationDate,
Long price,
String partNumber,
long manufactureCost,
UnitOfMeasure unitOfMeasure,
Person owner
) {
public Product (Long id,
String name,
Coordinates coordinates,
ZonedDateTime creationDate,
Long price,
String partNumber,
long manufactureCost,
UnitOfMeasure unitOfMeasure,
Person owner
) {
this.id = id;
this.name = name;
this.coordinates = coordinates;

View File

@ -1,20 +1,20 @@
package me.zinch.Lab6.Domain.models;
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.Lab6.Domain.exceptions.ValidationException;
import me.zinch.Lab6.Domain.validator.NotEmpty;
import java.io.Serializable;
import me.zinch.validator.NotEmpty;
import java.time.ZonedDateTime;
/**
* Represents a Data Transfer Object (DTO) for a product with attributes such as name, coordinates, price, part number, manufacture cost, unit of measure, and owner.
*/
public class ProductDTO implements Serializable {
public class ProductDTO {
@NotBlank(message = "ProductDTO: Строка name не может быть пустой или null")
@JsonProperty("name")
private String name; //Поле не может быть null, Строка не может быть пустой
@ -24,7 +24,7 @@ public class ProductDTO implements Serializable {
private Coordinates coordinates; //Поле не может быть null
@NotNull(message = "ProductDTO: Поле price не может быть null")
@Min(value = 1, message = "ProductDTO: Значение поля price должно быть больше 0")
@Min(value = 1, message = "Поле price не может быть null")
@JsonProperty("price")
private Long price; //Поле не может быть null, Значение поля должно быть больше 0
@ -44,8 +44,7 @@ public class ProductDTO implements Serializable {
@JsonProperty("owner")
private Person owner; //Поле не может быть null
public ProductDTO() {
}
public ProductDTO() {}
public ProductDTO(String name, Coordinates coordinates, Long price, String partNumber, long manufactureCost, UnitOfMeasure unitOfMeasure, Person owner) {
this.name = name;
@ -73,63 +72,55 @@ public class ProductDTO implements Serializable {
return name;
}
public void setName(String name) {
if (name == null || name.isEmpty()) throw new ValidationException("Строка name не может быть пустой или null");
this.name = name;
}
public Coordinates getCoordinates() {
return coordinates;
}
public void setCoordinates(Coordinates coordinates) {
if (coordinates == null) throw new ValidationException("Поле coordinates не может быть null");
this.coordinates = coordinates;
}
public Long getPrice() {
return price;
}
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 String getPartNumber() {
return partNumber;
}
public void setPartNumber(String partNumber) {
if (partNumber.isEmpty()) throw new ValidationException("Строка partNumber не может быть пустой");
if (partNumber.length() > 82) throw new ValidationException("Длина строки partNumber не должна быть больше 82");
this.partNumber = partNumber;
}
public long getManufactureCost() {
return manufactureCost;
}
public void setManufactureCost(long manufactureCost) {
this.manufactureCost = manufactureCost;
}
public UnitOfMeasure getUnitOfMeasure() {
return unitOfMeasure;
}
public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) {
if (unitOfMeasure == null) throw new ValidationException("Поле unitOfMeasure не может быть null");
this.unitOfMeasure = unitOfMeasure;
}
public Person getOwner() {
return owner;
}
public void setName(String name) {
this.name = name;
}
public void setCoordinates(Coordinates coordinates) {
this.coordinates = coordinates;
}
public void setPrice(Long price) {
this.price = price;
}
public void setPartNumber(String partNumber) {
this.partNumber = partNumber;
}
public void setManufactureCost(long manufactureCost) {
this.manufactureCost = manufactureCost;
}
public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) {
this.unitOfMeasure = unitOfMeasure;
}
public void setOwner(Person owner) {
if (owner == null) throw new ValidationException("Поле owner не может быть null");
this.owner = owner;
}
}

Some files were not shown because too many files have changed in this diff Show More