commit
1b041ce87e
27 changed files with 1239 additions and 1548 deletions
|
@ -1,6 +1,15 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="11" />
|
<annotationProcessing>
|
||||||
|
<profile name="Gradle Imported" enabled="true">
|
||||||
|
<outputRelativeToContentRoot value="true" />
|
||||||
|
<processorPath useClasspath="false">
|
||||||
|
<entry name="$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.22/9c08ea24c6eb714e2d6170e8122c069a0ba9aacf/lombok-1.18.22.jar" />
|
||||||
|
</processorPath>
|
||||||
|
<module name="Lab5.main" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
<bytecodeTargetLevel target="1.8" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
8
.idea/inspectionProfiles/Project_Default.xml
Normal file
8
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="SortedCollectionWithNonComparableKeys" enabled="true" level="WARNING" enabled_by_default="true">
|
||||||
|
<option name="IGNORE_TYPE_PARAMETERS" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
|
@ -4,7 +4,7 @@
|
||||||
<component name="FrameworkDetectionExcludesConfiguration">
|
<component name="FrameworkDetectionExcludesConfiguration">
|
||||||
<file type="web" url="file://$PROJECT_DIR$" />
|
<file type="web" url="file://$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
|
@ -3,7 +3,7 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "ru.erius"
|
group = "ru.erius"
|
||||||
version = "1.0"
|
version = "1.1"
|
||||||
val mainClass = "$group.${name.toLowerCase()}.$name"
|
val mainClass = "$group.${name.toLowerCase()}.$name"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
|
@ -13,6 +13,8 @@ repositories {
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
|
testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2")
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
|
||||||
|
compileOnly("org.projectlombok:lombok:1.18.22")
|
||||||
|
annotationProcessor("org.projectlombok:lombok:1.18.22")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.getByName<Test>("test") {
|
tasks.getByName<Test>("test") {
|
||||||
|
@ -23,6 +25,10 @@ tasks.compileJava {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.javadoc {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
tasks.jar {
|
tasks.jar {
|
||||||
manifest {
|
manifest {
|
||||||
attributes(
|
attributes(
|
||||||
|
@ -31,7 +37,3 @@ tasks.jar {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.javadoc {
|
|
||||||
options.encoding = "UTF-8"
|
|
||||||
}
|
|
|
@ -1,18 +1,24 @@
|
||||||
package ru.erius.lab5;
|
package ru.erius.lab5;
|
||||||
|
|
||||||
import ru.erius.lab5.cli.CommandParser;
|
import ru.erius.lab5.collection.Database;
|
||||||
import ru.erius.lab5.collection.PersonTreeSet;
|
import ru.erius.lab5.collection.PeopleDatabase;
|
||||||
|
import ru.erius.lab5.commandline.CommandLineHandler;
|
||||||
|
import ru.erius.lab5.commandline.PeopleDatabaseCommands;
|
||||||
|
|
||||||
public class Lab5 {
|
public class Lab5 {
|
||||||
/**
|
|
||||||
* Создание коллекции {@link PersonTreeSet PersonTreeSet}
|
|
||||||
* и парсера {@link CommandParser CommandParser}, запуск программы на выполнение
|
|
||||||
*
|
|
||||||
* @param args Аргументы командной строки
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
PersonTreeSet pts = new PersonTreeSet();
|
CommandLineHandler cmd = CommandLineHandler.getInstance();
|
||||||
CommandParser cmd = new CommandParser(pts);
|
|
||||||
|
PeopleDatabaseCommands.registerDatabaseCommands();
|
||||||
|
PeopleDatabase peopleDatabase = new PeopleDatabase();
|
||||||
|
try {
|
||||||
|
peopleDatabase.load();
|
||||||
|
} catch (Database.DatabaseLoadFailedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
PeopleDatabaseCommands.setPeopleDatabase(peopleDatabase);
|
||||||
|
|
||||||
cmd.start();
|
cmd.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,597 +0,0 @@
|
||||||
package ru.erius.lab5.cli;
|
|
||||||
|
|
||||||
import ru.erius.lab5.collection.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Класс, добавляющий возможность интерактивного взаимодействия пользователя с коллекцией через терминал,
|
|
||||||
* для своей работы требует экземпляр класса {@link PersonTreeSet PersonTreeSet}
|
|
||||||
*/
|
|
||||||
public class CommandParser {
|
|
||||||
/**
|
|
||||||
* Вспомогательная строка для создания красивого вывода
|
|
||||||
*/
|
|
||||||
private final static String LINE = "=============================================================================================================================================================================================";
|
|
||||||
/**
|
|
||||||
* Строка, выводимая при запуске программы
|
|
||||||
*/
|
|
||||||
private final static String GREETINGS = LINE + "\n" +
|
|
||||||
"Добро пожаловать в программу для управления коллекцией объектов в интерактивном режиме!\n" +
|
|
||||||
"Напишите help, чтобы увидеть доступные команды\n" +
|
|
||||||
"Напишите exit, чтобы выйти из программы\n" +
|
|
||||||
LINE + "\n";
|
|
||||||
/**
|
|
||||||
* Строка, выводимая при написании команды help
|
|
||||||
*/
|
|
||||||
private final static String HELP = LINE + "\n" +
|
|
||||||
"help: вывести справку по доступным командам\n" +
|
|
||||||
"info : вывести в стандартный поток вывода информацию о коллекции (тип, дата инициализации, количество элементов и т.д.)\n" +
|
|
||||||
"show : вывести в стандартный поток вывода все элементы коллекции в строковом представлении\n" +
|
|
||||||
"add [element] : добавить новый элемент в коллекцию\n" +
|
|
||||||
"update {id} [element] : обновить значение элемента коллекции, id которого равен заданному\n" +
|
|
||||||
"remove_by_id {id} : удалить элемент из коллекции по его id\n" +
|
|
||||||
"clear : очистить коллекцию\n" +
|
|
||||||
"save : сохранить коллекцию в файл\n" +
|
|
||||||
"execute_script {file_name} : считать и исполнить скрипт из указанного файла. В скрипте содержатся команды в таком же виде, в котором их вводит пользователь в интерактивном режиме.\n" +
|
|
||||||
"exit : завершить программу (без сохранения в файл)\n" +
|
|
||||||
"add_if_max [element] : добавить новый элемент в коллекцию, если его значение превышает значение наибольшего элемента этой коллекции\n" +
|
|
||||||
"add_if_min [element] : добавить новый элемент в коллекцию, если его значение меньше, чем у наименьшего элемента этой коллекции\n" +
|
|
||||||
"history : вывести последние 6 команд (без их аргументов)\n" +
|
|
||||||
"sum_of_height : вывести сумму значений поля height для всех элементов коллекции\n" +
|
|
||||||
"filter_contains_name {name} : вывести элементы, значение поля name которых содержит заданную подстроку\n" +
|
|
||||||
"print_field_descending_location : вывести значения поля location всех элементов в порядке убывания\n" +
|
|
||||||
LINE + "\n";
|
|
||||||
/**
|
|
||||||
* Коллекция, на основе которой функционирует данный класс
|
|
||||||
*/
|
|
||||||
private final PersonTreeSet personTreeSet;
|
|
||||||
/**
|
|
||||||
* Логическая переменная, хранящая состояние программы
|
|
||||||
*/
|
|
||||||
private boolean isActive = false;
|
|
||||||
/**
|
|
||||||
* Очередь входных потоков, используется для выполнения пользовательских скриптов,
|
|
||||||
* также требуется для реализации возможности рекурсивного выполнения скриптов
|
|
||||||
*/
|
|
||||||
private final Queue<InputStream> inputStreams = new LinkedList<>();
|
|
||||||
/**
|
|
||||||
* Reader, использующийся для чтения пользовательских команд или скриптов
|
|
||||||
*/
|
|
||||||
private BufferedReader reader;
|
|
||||||
/**
|
|
||||||
* Список последних 6 выполненных команд
|
|
||||||
*/
|
|
||||||
private final LinkedList<String> history = new LinkedList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор класса
|
|
||||||
*
|
|
||||||
* @param personTreeSet Коллекция типа {@link PersonTreeSet PersonTreeSet}
|
|
||||||
*/
|
|
||||||
public CommandParser(PersonTreeSet personTreeSet) {
|
|
||||||
this.personTreeSet = personTreeSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, открывающий поток ввода данных и запускающий программу,
|
|
||||||
* циклически вызывая функцию {@link #parse() parse}
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
System.out.println(GREETINGS);
|
|
||||||
reader = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
this.isActive = true;
|
|
||||||
do {
|
|
||||||
parse();
|
|
||||||
} while (this.isActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Основной метод, который ждет от пользователя ввода данных, после чего
|
|
||||||
* делит строку на команду и её аргументы, и в зависимости от них,
|
|
||||||
* вызывает нужную функцию на выполнение
|
|
||||||
*/
|
|
||||||
public void parse() {
|
|
||||||
String input = awaitInput("", "Что-то пошло не так").toLowerCase(Locale.ROOT);
|
|
||||||
String[] split = input.split(" ");
|
|
||||||
String command = split[0], argument = split.length > 1 ? split[1] : null;
|
|
||||||
switch (command) {
|
|
||||||
case "help": help(); break;
|
|
||||||
case "info": info(); break;
|
|
||||||
case "show": show(); break;
|
|
||||||
case "add": add(); break;
|
|
||||||
case "update": update(argument); break;
|
|
||||||
case "remove_by_id": removeByID(argument); break;
|
|
||||||
case "clear": clear(); break;
|
|
||||||
case "save": save(); break;
|
|
||||||
case "execute_script": executeScript(argument); break;
|
|
||||||
case "exit": exit(); break;
|
|
||||||
case "add_if_max": addIfMax(); break;
|
|
||||||
case "add_if_min": addIfMin(); break;
|
|
||||||
case "history": history(); break;
|
|
||||||
case "sum_of_height": sumOfHeight(); break;
|
|
||||||
case "filter_contains_name": filterContainsName(argument); break;
|
|
||||||
case "print_field_descending_location": printFieldDescendingLocation(); break;
|
|
||||||
default: unknown(); return;
|
|
||||||
}
|
|
||||||
updateHistory(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, обновляющий историю введенных команд
|
|
||||||
*
|
|
||||||
* @param command Введенная команда
|
|
||||||
*/
|
|
||||||
private void updateHistory(String command) {
|
|
||||||
history.add(command);
|
|
||||||
if (this.history.size() > 6)
|
|
||||||
this.history.removeFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе неизвестной команды
|
|
||||||
*/
|
|
||||||
public void unknown() {
|
|
||||||
System.err.println("Неизвестная команда. Введите help для отображения списка всех команд");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды help,
|
|
||||||
* печатает содержимое строки {@link #HELP HELP}
|
|
||||||
*/
|
|
||||||
public void help() {
|
|
||||||
System.out.println(CommandParser.HELP);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды info,
|
|
||||||
* печатает информацию о коллекции
|
|
||||||
*/
|
|
||||||
public void info() {
|
|
||||||
System.out.println(this.personTreeSet.info());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды show,
|
|
||||||
* печатает содержимое коллекции
|
|
||||||
*/
|
|
||||||
public void show() {
|
|
||||||
System.out.println("Вся коллекция PersonTreeSet:");
|
|
||||||
System.out.println(this.personTreeSet.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды add,
|
|
||||||
* пытается добавить новый элемент в коллекцию,
|
|
||||||
* печатает результат добавления
|
|
||||||
*/
|
|
||||||
public void add() {
|
|
||||||
Person person = createPerson();
|
|
||||||
boolean success = this.personTreeSet.add(person);
|
|
||||||
String msg = success ? "Объект был добавлен в коллекцию" : "Не удалось добавить объект";
|
|
||||||
System.out.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды update,
|
|
||||||
* пытается изменить существующий элемент коллекции по заданному id
|
|
||||||
* печатает результат изменения
|
|
||||||
*
|
|
||||||
* @param id Id человека
|
|
||||||
*/
|
|
||||||
public void update(long id) {
|
|
||||||
if (Person.getExistingPeople() < id || id <= 0) {
|
|
||||||
System.err.println("Не удалось найти объект с id " + id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Person person = createPerson();
|
|
||||||
boolean success = this.personTreeSet.update(id, person);
|
|
||||||
String msg = success ? "Объект был успешно изменен" : "Не удалось найти объект с id " + id;
|
|
||||||
System.out.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, аналогичный {@link #update(long) update(long)},
|
|
||||||
* пытается преобразовать данную строку в Long и вызвать {@link #update(long) update(long)}
|
|
||||||
*
|
|
||||||
* @param strId Id человека типа String
|
|
||||||
*/
|
|
||||||
public void update(String strId) {
|
|
||||||
Long id = idOrNull(strId);
|
|
||||||
if (id != null)
|
|
||||||
update(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды remove_by_id,
|
|
||||||
* пытается удалить существующий элемент из коллекции по заданному id
|
|
||||||
* печатает результат изменения
|
|
||||||
*
|
|
||||||
* @param id Id человека
|
|
||||||
*/
|
|
||||||
public void removeByID(long id) {
|
|
||||||
boolean success = this.personTreeSet.remove(id);
|
|
||||||
String msg = success ? "Объект был удален" : "Не удалось найти объект с id " + id;
|
|
||||||
System.out.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, аналогичный {@link #removeByID(long) update(long)},
|
|
||||||
* пытается преобразовать данную строку в Long и вызвать {@link #update(long) update(long)}
|
|
||||||
*
|
|
||||||
* @param strId Id человека типа String
|
|
||||||
*/
|
|
||||||
public void removeByID(String strId) {
|
|
||||||
Long id = idOrNull(strId);
|
|
||||||
if (id != null)
|
|
||||||
removeByID(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, преобразующий строковый id в Long,
|
|
||||||
* печатает результат преобразования
|
|
||||||
*
|
|
||||||
* @param strId Преобразуемая строка
|
|
||||||
* @return Преобразованная строка в Long или null при ошибке
|
|
||||||
*/
|
|
||||||
public Long idOrNull(String strId) {
|
|
||||||
long id;
|
|
||||||
if (strId == null) {
|
|
||||||
System.err.println("Параметр id должен быть указан");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
id = Long.parseLong(strId);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
System.err.println("Параметр id должен являться целым числом");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды clear, очищает коллекцию
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
this.personTreeSet.clear();
|
|
||||||
System.out.println("Коллекция успешно очищена");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды save,
|
|
||||||
* пытается сохранить коллекцию в файл,
|
|
||||||
* печатает результат сохранения
|
|
||||||
*/
|
|
||||||
public void save() {
|
|
||||||
boolean success = this.personTreeSet.save();
|
|
||||||
String msg = success ? "Коллекция была сохранена в файле " + this.personTreeSet.getFile().getAbsolutePath()
|
|
||||||
: "Не удалось сохранить коллекцию (обычно причина пишется при запуске программы)";
|
|
||||||
System.out.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды execute_script,
|
|
||||||
* пытается выполнить скрипт, написанный в файле fileName,
|
|
||||||
* печатает результат выполнения скрипта
|
|
||||||
*
|
|
||||||
* @param fileName Имя или путь к файлу
|
|
||||||
*/
|
|
||||||
public void executeScript(String fileName) {
|
|
||||||
if (fileName == null || fileName.isEmpty()) {
|
|
||||||
System.err.println("Параметр file_name должен быть указан");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
File file = new File(fileName);
|
|
||||||
if (!file.exists() || file.isDirectory()) {
|
|
||||||
System.err.println("Не удалось найти указанный файл со скриптом");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InputStream stream;
|
|
||||||
try {
|
|
||||||
stream = new FileInputStream(file);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
System.err.println("Файл не найден");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
newInputStream(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды exit,
|
|
||||||
* заканчивает выполнение программы, предлагая перед этим сохранить коллекцию
|
|
||||||
*/
|
|
||||||
public void exit() {
|
|
||||||
if (this.personTreeSet.getFile() != null) {
|
|
||||||
String answer = awaitInput("Сохранить коллекцию в файл? (Y - да, N - нет)", "Введите Y или N",
|
|
||||||
input -> input.toUpperCase(Locale.ROOT).equals("Y") || input.toUpperCase(Locale.ROOT).equals("N"),
|
|
||||||
input -> input.toUpperCase(Locale.ROOT));
|
|
||||||
if (answer.equals("Y"))
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
System.out.println("Выход из программы...");
|
|
||||||
this.isActive = false;
|
|
||||||
try {
|
|
||||||
reader.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды add_if_max,
|
|
||||||
* пытается добавить новый элемент в коллекцию, только если он больше максимального элемента в коллекции
|
|
||||||
* печатает результат добавления
|
|
||||||
*/
|
|
||||||
public void addIfMax() {
|
|
||||||
Person person = createPerson();
|
|
||||||
boolean success = this.personTreeSet.addIfMax(person);
|
|
||||||
String msg = success ? "Объект был добавлен в коллекцию" : "Не удалось добавить объект";
|
|
||||||
System.out.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды add_if_min,
|
|
||||||
* пытается добавить новый элемент в коллекцию, только если он меньше минимального элемента в коллекции
|
|
||||||
* печатает результат добавления
|
|
||||||
*/
|
|
||||||
public void addIfMin() {
|
|
||||||
Person person = createPerson();
|
|
||||||
boolean success = this.personTreeSet.addIfMin(person);
|
|
||||||
String msg = success ? "Объект был добавлен в коллекцию" : "Не удалось добавить объект";
|
|
||||||
System.out.println(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды history,
|
|
||||||
* печатает историю последних 6 введенных команд
|
|
||||||
*/
|
|
||||||
public void history() {
|
|
||||||
System.out.println("История последних 6 команд:");
|
|
||||||
this.history.forEach(System.out::println);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вызываемый при вводе команды sum_of_height,
|
|
||||||
* печатает суммарный рост всех людей в коллекции
|
|
||||||
*/
|
|
||||||
public void sumOfHeight() {
|
|
||||||
int sum = this.personTreeSet.sumOfHeight();
|
|
||||||
System.out.println("Сумма ростов всех людей в коллекции: " + sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, фильтрующий и печатающий всех людей, в имени которых имеется строка name,
|
|
||||||
* проверка не чувствительна к регистру
|
|
||||||
*
|
|
||||||
* @param name Строка, по которой происходит фильтрация
|
|
||||||
*/
|
|
||||||
public void filterContainsName(String name) {
|
|
||||||
if (name == null || name.isEmpty()) {
|
|
||||||
System.err.println("Параметр name должен быть указан");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<Person> people = this.personTreeSet.filterContainsName(name);
|
|
||||||
System.out.println("Список людей, в имени которых содержится " + name + ":");
|
|
||||||
people.forEach(System.out::println);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, печатающий отсортированный по убыванию список местоположений всех людей в коллекции
|
|
||||||
*/
|
|
||||||
public void printFieldDescendingLocation() {
|
|
||||||
List<Location> locations = this.personTreeSet.fieldDescendingLocation();
|
|
||||||
System.out.println("Список локаций в порядке убывания:");
|
|
||||||
locations.forEach(System.out::println);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, интерактивно создающий нового человека, получая каждое новое значение из потока ввода {@link #reader reader}
|
|
||||||
*
|
|
||||||
* @return Новый экземпляр класса {@link Person Person}
|
|
||||||
*/
|
|
||||||
private Person createPerson() {
|
|
||||||
System.out.println("Создание нового объекта класса Person");
|
|
||||||
Person person = new Person();
|
|
||||||
person.setName(
|
|
||||||
awaitInput("Введите имя:", "Введите непустую строку",
|
|
||||||
input -> !input.isEmpty())
|
|
||||||
);
|
|
||||||
person.setHeight(
|
|
||||||
awaitInput("Введите рост:", "Введите целое число, большее нуля",
|
|
||||||
input -> {
|
|
||||||
try {
|
|
||||||
int num = Integer.parseInt(input);
|
|
||||||
return num > 0;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, Integer::parseInt)
|
|
||||||
);
|
|
||||||
person.setPassportID(
|
|
||||||
awaitInput("Введите номер паспорта:", "Введите как минимум 8 символов",
|
|
||||||
input -> input.length() >= 8)
|
|
||||||
);
|
|
||||||
person.setEyeColor(
|
|
||||||
awaitInput("Введите цвет глаз (BLACK, ORANGE, BROWN):", "Введите один из предложенных цветов",
|
|
||||||
input -> Color.isColorValid(input.toUpperCase(Locale.ROOT)), input -> Color.valueOf(input.toUpperCase(Locale.ROOT)))
|
|
||||||
);
|
|
||||||
person.setNationality(
|
|
||||||
awaitInput("Введите страну (UNITED_KINGDOM, GERMANY, CHINA, THAILAND, JAPAN):", "Введите одну из предлженных стран",
|
|
||||||
input -> Country.isCountryValid(input.toUpperCase(Locale.ROOT)), input -> Country.valueOf(input.toUpperCase(Locale.ROOT)))
|
|
||||||
);
|
|
||||||
person.setCoordinates(createCoordinates());
|
|
||||||
person.setLocation(createLocation());
|
|
||||||
return person;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, интерактивно создающий новые координаты, получая каждое новое значение из потока ввода {@link #reader reader}
|
|
||||||
*
|
|
||||||
* @return Новый экземпляр класса {@link Coordinates Coordinates}
|
|
||||||
*/
|
|
||||||
private Coordinates createCoordinates() {
|
|
||||||
System.out.println("Создание нового объекта класса Coordinates");
|
|
||||||
Coordinates coordinates = new Coordinates();
|
|
||||||
coordinates.setX(
|
|
||||||
awaitInput("Введите x:", "Введите дробное число",
|
|
||||||
input -> {
|
|
||||||
try {
|
|
||||||
Float.parseFloat(input);
|
|
||||||
return true;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, Float::parseFloat)
|
|
||||||
);
|
|
||||||
coordinates.setY(
|
|
||||||
awaitInput("Введите y:", "Введите дробное число, большее -816",
|
|
||||||
input -> {
|
|
||||||
try {
|
|
||||||
float num = Float.parseFloat(input);
|
|
||||||
return num > -816F;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, Float::parseFloat)
|
|
||||||
);
|
|
||||||
return coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, интерактивно создающий новое местоположение, получая каждое новое значение из потока ввода {@link #reader reader}
|
|
||||||
*
|
|
||||||
* @return Новый экземпляр класса {@link Location Location}
|
|
||||||
*/
|
|
||||||
private Location createLocation() {
|
|
||||||
System.out.println("Создание нового объекта класса Location");
|
|
||||||
Location location = new Location();
|
|
||||||
location.setX(
|
|
||||||
awaitInput("Введите x:", "Введите дробное число",
|
|
||||||
input -> {
|
|
||||||
try {
|
|
||||||
Double.parseDouble(input);
|
|
||||||
return true;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, Double::parseDouble)
|
|
||||||
);
|
|
||||||
location.setY(
|
|
||||||
awaitInput("Введите y:", "Введите дробное число",
|
|
||||||
input -> {
|
|
||||||
try {
|
|
||||||
Double.parseDouble(input);
|
|
||||||
return true;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, Float::parseFloat)
|
|
||||||
);
|
|
||||||
location.setZ(
|
|
||||||
awaitInput("Введите z:", "Введите целое число",
|
|
||||||
input -> {
|
|
||||||
try {
|
|
||||||
Long.parseLong(input);
|
|
||||||
return true;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}, Long::parseLong)
|
|
||||||
);
|
|
||||||
location.setName(
|
|
||||||
awaitInput("Введите название:", "Введите непустую строку",
|
|
||||||
input -> !input.isEmpty())
|
|
||||||
);
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, ожидающий ввода из потока ввода {@link #reader reader} и возвращающий результат,,
|
|
||||||
* печатает запрос msg перед ожиданием данных (если их вводит пользователь),
|
|
||||||
* печатает ошибку err, если при вводе данных произошла ошибка
|
|
||||||
*
|
|
||||||
* @param msg Строка, печатающаяся как запрос данных от пользователя
|
|
||||||
* @param err Строка, печатающаяся во время ошибки
|
|
||||||
*
|
|
||||||
* @return Строка из потока ввода
|
|
||||||
*/
|
|
||||||
private String awaitInput(String msg, String err) {
|
|
||||||
return awaitInput(msg, err, input -> true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, ожидающий ввода из потока ввода {@link #reader reader } и возвращающий результат,,
|
|
||||||
* печатает запрос msg перед ожиданием данных (если их вводит пользователь),
|
|
||||||
* печатает ошибку err, если введенные данные не соответствуют предикату predicate
|
|
||||||
*
|
|
||||||
* @param msg Строка, печатающаяся как запрос данных от пользователя
|
|
||||||
* @param err Строка, печатающаяся при несоответствии ввода предикату
|
|
||||||
* @param predicate Предикат, определяющий валидность введенных данных
|
|
||||||
*
|
|
||||||
* @return Строка из потока ввода
|
|
||||||
*/
|
|
||||||
private String awaitInput(String msg, String err, Predicate<String> predicate) {
|
|
||||||
String input = null;
|
|
||||||
do {
|
|
||||||
if (inputStreams.isEmpty())
|
|
||||||
System.out.print(msg + "\n>>> ");
|
|
||||||
try {
|
|
||||||
input = reader.readLine();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
if (input == null) {
|
|
||||||
prevInputStream();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (predicate.test(input.trim()))
|
|
||||||
break;
|
|
||||||
else
|
|
||||||
System.err.println(err);
|
|
||||||
} while (true);
|
|
||||||
System.out.println();
|
|
||||||
return input.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, ожидающий ввода из потока ввода {@link #reader reader} и возвращающий результат,
|
|
||||||
* печатает запрос msg перед ожиданием данных (если их вводит пользователь),
|
|
||||||
* печатает ошибку err, если введенные данные не соответствуют предикату predicate,
|
|
||||||
* преобразует результат в тип T в соответствии с функцией transform
|
|
||||||
*
|
|
||||||
* @param msg Строка, печатающаяся как запрос данных от пользователя
|
|
||||||
* @param err Строка, печатающаяся при несоответствии ввода предикату
|
|
||||||
* @param predicate Предикат, определяющий валидность введенных данных
|
|
||||||
* @param <T> Тип, к которому будет приведен результат
|
|
||||||
* @param transform Функция, преобразующая результат в тип T
|
|
||||||
*
|
|
||||||
* @return Результат типа T
|
|
||||||
*/
|
|
||||||
private <T> T awaitInput(String msg, String err, Predicate<String> predicate, Function<String, T> transform) {
|
|
||||||
String result = awaitInput(msg, err, predicate);
|
|
||||||
return transform.apply(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, меняющий текущий поток ввода на stream и добавляет его в очередь {@link #inputStreams inputStreams}
|
|
||||||
*
|
|
||||||
* @param stream Новый поток ввода
|
|
||||||
*/
|
|
||||||
private void newInputStream(InputStream stream) {
|
|
||||||
this.reader = new BufferedReader(new InputStreamReader(stream));
|
|
||||||
this.inputStreams.add(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, убирающий текущий поток ввода из очереди {@link #inputStreams inputStreams}
|
|
||||||
* и меняющий его либо на следующий в очереди поток, либо на System.in, если очередь пуста
|
|
||||||
*/
|
|
||||||
private void prevInputStream() {
|
|
||||||
inputStreams.poll();
|
|
||||||
InputStream stream = inputStreams.isEmpty() ? System.in : inputStreams.peek();
|
|
||||||
this.reader = new BufferedReader(new InputStreamReader(stream));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package ru.erius.lab5.collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Перечисление цветов
|
|
||||||
*/
|
|
||||||
public enum Color {
|
|
||||||
BLACK,
|
|
||||||
ORANGE,
|
|
||||||
BROWN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, проверяющий, можно ли из данной строки получить значение типа Color
|
|
||||||
*
|
|
||||||
* @param strColor Проверяемая строка
|
|
||||||
*
|
|
||||||
* @return true, если из строки можно получить значение типа Color, иначе false
|
|
||||||
*/
|
|
||||||
public static boolean isColorValid(String strColor) {
|
|
||||||
try {
|
|
||||||
Color.valueOf(strColor);
|
|
||||||
return true;
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package ru.erius.lab5.collection;
|
|
||||||
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Класс данных координат
|
|
||||||
*/
|
|
||||||
public class Coordinates {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Координата X типа float
|
|
||||||
*/
|
|
||||||
private float x;
|
|
||||||
/**
|
|
||||||
* Координата Y типа float, значение должно быть больше -816
|
|
||||||
*/
|
|
||||||
private float y;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор без параметров, задаёт значения всех полей по умолчанию,
|
|
||||||
* x = 0, y = 0
|
|
||||||
*/
|
|
||||||
public Coordinates() {
|
|
||||||
this.x = 0F;
|
|
||||||
this.y = 0F;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор с параметрами
|
|
||||||
*
|
|
||||||
* @param x Координата X
|
|
||||||
* @param y Координата Y
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если Y меньше или равен -816
|
|
||||||
*/
|
|
||||||
public Coordinates(float x, float y) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX(float x) {
|
|
||||||
this.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля y
|
|
||||||
*
|
|
||||||
* @param y Координата Y
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если Y меньше или равен -816
|
|
||||||
*/
|
|
||||||
public void setY(float y) {
|
|
||||||
this.y = y;
|
|
||||||
if (y <= -816)
|
|
||||||
throw new IllegalArgumentException("Поле y класса Coordinates должно быть больше -816");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (this == other) return true;
|
|
||||||
if (other == null || getClass() != other.getClass()) return false;
|
|
||||||
|
|
||||||
Coordinates that = (Coordinates) other;
|
|
||||||
|
|
||||||
if (Float.compare(that.x, x) != 0) return false;
|
|
||||||
return Float.compare(that.y, y) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = (x != 0.0f ? Float.floatToIntBits(x) : 0);
|
|
||||||
result = 31 * result + (y != 0.0f ? Float.floatToIntBits(y) : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format(Locale.US, "Coordinates(x=%f, y=%f)", this.x, this.y);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package ru.erius.lab5.collection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Перечисление стран
|
|
||||||
*/
|
|
||||||
public enum Country {
|
|
||||||
UNITED_KINGDOM,
|
|
||||||
GERMANY,
|
|
||||||
CHINA,
|
|
||||||
THAILAND,
|
|
||||||
JAPAN;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, проверяющий, можно ли из данной строки получить значение типа Country
|
|
||||||
*
|
|
||||||
* @param strCountry Проверяемая строка
|
|
||||||
*
|
|
||||||
* @return true, если из строки можно получить значение типа Country, иначе false
|
|
||||||
*/
|
|
||||||
public static boolean isCountryValid(String strCountry) {
|
|
||||||
try {
|
|
||||||
Country.valueOf(strCountry);
|
|
||||||
return true;
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
57
src/main/java/ru/erius/lab5/collection/Database.java
Normal file
57
src/main/java/ru/erius/lab5/collection/Database.java
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package ru.erius.lab5.collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Интерфейс баз данных с методами {@link #load()} и {@link #save()}
|
||||||
|
*/
|
||||||
|
public interface Database {
|
||||||
|
|
||||||
|
void load() throws DatabaseLoadFailedException;
|
||||||
|
|
||||||
|
void save() throws DatabaseSaveFailedException;
|
||||||
|
|
||||||
|
class DatabaseLoadFailedException extends Exception {
|
||||||
|
|
||||||
|
public DatabaseLoadFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseLoadFailedException(String message, String path) {
|
||||||
|
super(String.format(message, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseLoadFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseLoadFailedException(String message, String path, Throwable cause) {
|
||||||
|
super(String.format(message, path), cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseLoadFailedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DatabaseSaveFailedException extends Exception {
|
||||||
|
|
||||||
|
public DatabaseSaveFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseSaveFailedException(String message, String path) {
|
||||||
|
super(String.format(message, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseSaveFailedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseSaveFailedException(String message, String path, Throwable cause) {
|
||||||
|
super(String.format(message, path), cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseSaveFailedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,160 +0,0 @@
|
||||||
package ru.erius.lab5.collection;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Класс данных местоположения, реализует сортировку по умолчанию
|
|
||||||
* по имени и расстоянию до точки (0; 0; 0)
|
|
||||||
*/
|
|
||||||
public class Location implements Comparable<Location> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Координата X типа double
|
|
||||||
*/
|
|
||||||
private double x;
|
|
||||||
/**
|
|
||||||
* Координата Y типа float
|
|
||||||
*/
|
|
||||||
private float y;
|
|
||||||
/**
|
|
||||||
* Координата Z типа Long, не может быть null
|
|
||||||
*/
|
|
||||||
private Long z;
|
|
||||||
/**
|
|
||||||
* Имя локации, может быть null
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор без параметров, задаёт значения всех полей по умолчанию,
|
|
||||||
* x = 0, y = 0, z = 0, name = null
|
|
||||||
*/
|
|
||||||
public Location() {
|
|
||||||
this.x = 0D;
|
|
||||||
this.y = 0F;
|
|
||||||
this.z = 0L;
|
|
||||||
this.name = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор с параметрами
|
|
||||||
*
|
|
||||||
* @param x Координата X
|
|
||||||
* @param y Координата Y
|
|
||||||
* @param z Координата Z
|
|
||||||
* @param name Имя локации
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Данное исключение будет брошено в следующих случаях:
|
|
||||||
* Z является null,
|
|
||||||
* name является пустой строкой
|
|
||||||
*/
|
|
||||||
public Location(double x, float y, Long z, String name) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.setZ(z);
|
|
||||||
this.setName(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getZ() {
|
|
||||||
return z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setX(double x) {
|
|
||||||
this.x = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setY(float y) {
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля z
|
|
||||||
* @param z Координата Z
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если Z является null
|
|
||||||
*/
|
|
||||||
public void setZ(Long z) {
|
|
||||||
this.z = z;
|
|
||||||
if (z == null)
|
|
||||||
throw new IllegalArgumentException("Поле z класса Location не может быть null");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля name
|
|
||||||
* @param name Имя локации
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если name является пустой строкой
|
|
||||||
*/
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
if (name.isEmpty())
|
|
||||||
throw new IllegalArgumentException("Поле name класса Location не может быть пустым");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Переопределенный метод сравнения двух местоположений,
|
|
||||||
* сравнение производится по имени локации и расстоянию до точки (0; 0; 0)
|
|
||||||
*
|
|
||||||
* @param other Объект для сравнения
|
|
||||||
* @return Целое число - результат сравнения
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int compareTo(Location other) {
|
|
||||||
return Comparator.comparing(Location::getName)
|
|
||||||
.thenComparing(Location::distance)
|
|
||||||
.compare(this, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, вычисляющий расстояние до точки (0; 0; 0)
|
|
||||||
*
|
|
||||||
* @return Расстояние типа double
|
|
||||||
*/
|
|
||||||
private double distance() {
|
|
||||||
return Math.sqrt(x * x + y * y + z * z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (this == other) return true;
|
|
||||||
if (other == null || getClass() != other.getClass()) return false;
|
|
||||||
|
|
||||||
Location location = (Location) other;
|
|
||||||
|
|
||||||
if (Double.compare(location.x, x) != 0) return false;
|
|
||||||
if (Float.compare(location.y, y) != 0) return false;
|
|
||||||
if (!z.equals(location.z)) return false;
|
|
||||||
return Objects.equals(name, location.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result;
|
|
||||||
long temp;
|
|
||||||
temp = Double.doubleToLongBits(x);
|
|
||||||
result = (int) (temp ^ (temp >>> 32));
|
|
||||||
result = 31 * result + (y != 0.0f ? Float.floatToIntBits(y) : 0);
|
|
||||||
result = 31 * result + z.hashCode();
|
|
||||||
result = 31 * result + (name != null ? name.hashCode() : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format(Locale.US, "Location(x=%f, y=%f, z=%s, name=%s)", this.x, this.y, this.z, this.name);
|
|
||||||
}
|
|
||||||
}
|
|
138
src/main/java/ru/erius/lab5/collection/PeopleDatabase.java
Normal file
138
src/main/java/ru/erius/lab5/collection/PeopleDatabase.java
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package ru.erius.lab5.collection;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import ru.erius.lab5.data.Person;
|
||||||
|
import ru.erius.lab5.parser.LocalDateAdapter;
|
||||||
|
|
||||||
|
import javax.xml.bind.JAXBContext;
|
||||||
|
import javax.xml.bind.JAXBException;
|
||||||
|
import javax.xml.bind.Marshaller;
|
||||||
|
import javax.xml.bind.Unmarshaller;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
import javax.xml.bind.annotation.XmlRootElement;
|
||||||
|
import javax.xml.bind.annotation.XmlTransient;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс базы данных людей, реализующий интерфейс Database
|
||||||
|
* @see ru.erius.lab5.collection.Database
|
||||||
|
*/
|
||||||
|
@XmlRootElement
|
||||||
|
public class PeopleDatabase implements Database {
|
||||||
|
|
||||||
|
private final static String
|
||||||
|
ENV_VAR = "LAB5_PATH",
|
||||||
|
DEFAULT_FILE_NAME = "lab5.xml",
|
||||||
|
TYPE = "TreeSet";
|
||||||
|
|
||||||
|
@XmlTransient
|
||||||
|
private JAXBContext context;
|
||||||
|
@XmlTransient
|
||||||
|
private File file;
|
||||||
|
@Getter @XmlElement(name = "person")
|
||||||
|
private TreeSet<Person> collection = new TreeSet<>();
|
||||||
|
@Getter @XmlJavaTypeAdapter(LocalDateAdapter.class) @XmlElement(name = "initDate")
|
||||||
|
private LocalDate initDate = LocalDate.now();
|
||||||
|
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
this.context = JAXBContext.newInstance(PeopleDatabase.class);
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String info() {
|
||||||
|
return String.format("Тип коллекции: %s \n" +
|
||||||
|
"Дата инициализации: %s \n" +
|
||||||
|
"Количество элементов: %d \n",
|
||||||
|
TYPE, this.initDate, this.collection.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, инициализирующий базу данных из файла, находящемся по пути, указанном в
|
||||||
|
* переменной окружения {@link #ENV_VAR}
|
||||||
|
*
|
||||||
|
* @throws Database.DatabaseLoadFailedException если переменная окружения {@link #ENV_VAR} не задана,
|
||||||
|
* файла не существует, либо отсутствуют права на запись или чтение
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void load() throws Database.DatabaseLoadFailedException {
|
||||||
|
System.out.println("Инициализация коллекции из файла...");
|
||||||
|
|
||||||
|
String path = System.getenv(ENV_VAR);
|
||||||
|
if (path == null)
|
||||||
|
throw new DatabaseLoadFailedException("Не найдена переменная окружения LAB5_PATH");
|
||||||
|
file = new File(path);
|
||||||
|
|
||||||
|
if (!file.exists())
|
||||||
|
throw new DatabaseLoadFailedException("Файл %s не был найден. Поменяйте значение переменной окружения LAB5_PATH", path);
|
||||||
|
if (!file.canRead())
|
||||||
|
throw new DatabaseLoadFailedException("У вас нет прав на чтение файла %s", path);
|
||||||
|
if (!file.canWrite())
|
||||||
|
throw new DatabaseLoadFailedException("У вас нет прав на запись в файл %s", path);
|
||||||
|
if (file.isFile())
|
||||||
|
System.out.println("Файл успешно найден");
|
||||||
|
else
|
||||||
|
file = createFile(file);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Unmarshaller unmarshaller = context.createUnmarshaller();
|
||||||
|
PeopleDatabase pd = (PeopleDatabase) unmarshaller.unmarshal(file);
|
||||||
|
this.collection = pd.collection;
|
||||||
|
this.initDate = pd.initDate;
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
throw new DatabaseLoadFailedException("Не удалось загрузить коллекцию из файла %s", file.getPath(), e);
|
||||||
|
}
|
||||||
|
System.out.println("Инициализация успешно выполнена");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, сохраняющий базу данных в файл, находящемся по пути, указанном в
|
||||||
|
* переменной окружения {@link #ENV_VAR}
|
||||||
|
*
|
||||||
|
* @throws Database.DatabaseSaveFailedException если переменная окружения {@link #ENV_VAR} не задана,
|
||||||
|
* файла не существует, либо отсутствуют права на запись или чтение, или если структура xml файла
|
||||||
|
* была каким-либо образом нарушена
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void save() throws Database.DatabaseSaveFailedException {
|
||||||
|
if (file == null || context == null)
|
||||||
|
throw new DatabaseSaveFailedException("Не удалось сохранить коллекцию");
|
||||||
|
try {
|
||||||
|
Marshaller marshaller = context.createMarshaller();
|
||||||
|
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
|
||||||
|
marshaller.marshal(this, file);
|
||||||
|
} catch (JAXBException e) {
|
||||||
|
throw new DatabaseSaveFailedException("Не удалось сохранить коллекцию в файл %s", file.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File createFile(File file) {
|
||||||
|
System.out.println("Директория успешно найдена");
|
||||||
|
file = new File(file.getAbsolutePath() + "/" + DEFAULT_FILE_NAME);
|
||||||
|
boolean exists;
|
||||||
|
try {
|
||||||
|
exists = file.createNewFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.err.println("Что-то пошло не так");
|
||||||
|
System.exit(-1);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
System.out.println(exists ? "Файл не найден, создание файла " + DEFAULT_FILE_NAME : "Файл успешно найден");
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder("PeopleDatabase(\n");
|
||||||
|
this.collection.forEach(p -> sb.append("\t").append(p).append("\n"));
|
||||||
|
sb.append(")");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,337 +0,0 @@
|
||||||
package ru.erius.lab5.collection;
|
|
||||||
|
|
||||||
import java.time.DateTimeException;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Класс данных человека, реализует сортировку по умолчанию по имени, номеру паспорта,
|
|
||||||
* росту, национальности, местоположению и цвету глаз
|
|
||||||
*/
|
|
||||||
public class Person implements Comparable<Person> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Количество созданных людей, используется для задания
|
|
||||||
* уникального id для каждого объекта данного класса
|
|
||||||
*/
|
|
||||||
private static long existingPeople = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Регулярное выражение для валидации и создания
|
|
||||||
* экземпляров данного класса из строк
|
|
||||||
*/
|
|
||||||
private final static Pattern PERSON_REGEX = Pattern.compile(
|
|
||||||
"Person\\(\\s*id=(?<id>\\d+),\\s*name=(?<name>\\S+)," +
|
|
||||||
"\\s*coordinates=Coordinates\\(x=(?<coordX>[+-]?(\\d+([.,]\\d*)?|[.,]\\d+)),\\s*y=(?<coordY>[+-]?(\\d+([.,]\\d*)?|[.,]\\d+))\\)," +
|
|
||||||
"\\s*creationDate=(?<creationDate>\\d{4}[\\-.]\\d{2}[\\-.]\\d{2}),\\s*height=(?<height>\\d+),\\s*passportID=(?<passportID>\\S{8,})," +
|
|
||||||
"\\s*eyeColor=(?<eyeColor>\\w+),\\s*nationality=(?<nationality>\\w+),\\" +
|
|
||||||
"s*location=Location\\(x=(?<locX>[+-]?(\\d+([.,]\\d*)?|[.,]\\d+)),\\s*y=(?<locY>[+-]?(\\d+([.,]\\d*)?|[.,]\\d+))," +
|
|
||||||
"\\s*z=(?<locZ>[+-]?\\d+),\\s*name=(?<locName>\\S+)\\)\\)"
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Id человека, не может быть null, значение поля должно быть больше 0,
|
|
||||||
* значение этого поля должно быть уникальным, значение этого поля должно генерироваться автоматически
|
|
||||||
*/
|
|
||||||
private Long id;
|
|
||||||
/**
|
|
||||||
* Имя человека, не может быть null, строка не может быть пустой
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
/**
|
|
||||||
* Координаты человека, не может быть null
|
|
||||||
*/
|
|
||||||
private Coordinates coordinates;
|
|
||||||
/**
|
|
||||||
* Дата создания объекта, не может быть null, значение этого поля должно генерироваться автоматически
|
|
||||||
*/
|
|
||||||
private LocalDate creationDate;
|
|
||||||
/**
|
|
||||||
* Рост человека, может быть null, значение поля должно быть больше 0
|
|
||||||
*/
|
|
||||||
private Integer height;
|
|
||||||
/**
|
|
||||||
* Номер паспорта человека, длина строки должна быть не меньше 8, поле может быть null
|
|
||||||
*/
|
|
||||||
private String passportID;
|
|
||||||
/**
|
|
||||||
* Цвет глаз человека, не может быть null
|
|
||||||
*/
|
|
||||||
private Color eyeColor;
|
|
||||||
/**
|
|
||||||
* Национальность человека, не может быть null
|
|
||||||
*/
|
|
||||||
private Country nationality;
|
|
||||||
/**
|
|
||||||
* Местоположение человека, может быть null
|
|
||||||
*/
|
|
||||||
private Location location;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор без параметров, задаёт значения всех полей по умолчанию,
|
|
||||||
* name = "None", coordinates = new Coordinates(), height = 1,
|
|
||||||
* passportID = null, eyeColor = Color.BLACK, nationality = Country.UNITED_KINGDOM
|
|
||||||
*/
|
|
||||||
public Person() {
|
|
||||||
this.id = ++existingPeople;
|
|
||||||
this.creationDate = LocalDate.now();
|
|
||||||
this.location = null;
|
|
||||||
this.name = "None";
|
|
||||||
this.coordinates = new Coordinates();
|
|
||||||
this.height = 1;
|
|
||||||
this.passportID = null;
|
|
||||||
this.eyeColor = Color.BLACK;
|
|
||||||
this.nationality = Country.UNITED_KINGDOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор с параметрами
|
|
||||||
*
|
|
||||||
* @param name Имя человека
|
|
||||||
* @param coordinates Координаты человека
|
|
||||||
* @param height Высота человека
|
|
||||||
* @param passportID Номер паспорта человека
|
|
||||||
* @param eyeColor Цвет глаз человека
|
|
||||||
* @param nationality Национальность человека
|
|
||||||
* @param location Местоположение человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Данное исключение будет брошено в следующих случаях:
|
|
||||||
* name является null или пустой строкой,
|
|
||||||
* coordinates является null,
|
|
||||||
* height меньше 0,
|
|
||||||
* Длина passportID меньше 8 символов,
|
|
||||||
* eyeColor является null,
|
|
||||||
* nationality является null
|
|
||||||
*/
|
|
||||||
public Person(String name, Coordinates coordinates, Integer height, String passportID,
|
|
||||||
Color eyeColor, Country nationality, Location location) {
|
|
||||||
this.id = ++existingPeople;
|
|
||||||
this.creationDate = LocalDate.now();
|
|
||||||
this.location = location;
|
|
||||||
this.setName(name);
|
|
||||||
this.setCoordinates(coordinates);
|
|
||||||
this.setHeight(height);
|
|
||||||
this.setPassportID(passportID);
|
|
||||||
this.setEyeColor(eyeColor);
|
|
||||||
this.setNationality(nationality);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long getExistingPeople() {
|
|
||||||
return existingPeople;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coordinates getCoordinates() {
|
|
||||||
return coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassportID() {
|
|
||||||
return passportID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getEyeColor() {
|
|
||||||
return eyeColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Country getNationality() {
|
|
||||||
return nationality;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getLocation() {
|
|
||||||
return location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля name
|
|
||||||
*
|
|
||||||
* @param name Имя человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если имя является null или пустой строкой
|
|
||||||
*/
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
if (name == null || name.isEmpty())
|
|
||||||
throw new IllegalArgumentException("Поле name класса Person не может быть null или пустым");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля coordinates
|
|
||||||
*
|
|
||||||
* @param coordinates Координаты человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если объект координат является null
|
|
||||||
*/
|
|
||||||
public void setCoordinates(Coordinates coordinates) {
|
|
||||||
this.coordinates = coordinates;
|
|
||||||
if (coordinates == null)
|
|
||||||
throw new IllegalArgumentException("Поле coordinates класса Person не может быть null");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля height
|
|
||||||
*
|
|
||||||
* @param height Рост человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если рост меньше 0
|
|
||||||
*/
|
|
||||||
public void setHeight(Integer height) {
|
|
||||||
this.height = height;
|
|
||||||
if (height <= 0)
|
|
||||||
throw new IllegalArgumentException("Поле height класса Person должно быть больше 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля passportID
|
|
||||||
*
|
|
||||||
* @param passportID Номер паспорта человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если номер паспорта меньше 8 символов в длину
|
|
||||||
*/
|
|
||||||
public void setPassportID(String passportID) {
|
|
||||||
this.passportID = passportID;
|
|
||||||
if (passportID.length() < 8)
|
|
||||||
throw new IllegalArgumentException("Поле passportID класса Person не может быть меньше 8 символов в длину");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля eyeColor
|
|
||||||
*
|
|
||||||
* @param eyeColor Координаты человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если цвет глаз является null
|
|
||||||
*/
|
|
||||||
public void setEyeColor(Color eyeColor) {
|
|
||||||
this.eyeColor = eyeColor;
|
|
||||||
if (eyeColor == null)
|
|
||||||
throw new IllegalArgumentException("Поле eyeColor класса Person не может быть null");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сеттер для поля nationality
|
|
||||||
*
|
|
||||||
* @param nationality Координаты человека
|
|
||||||
*
|
|
||||||
* @throws IllegalArgumentException Если национальность является null
|
|
||||||
*/
|
|
||||||
public void setNationality(Country nationality) {
|
|
||||||
this.nationality = nationality;
|
|
||||||
if (nationality == null)
|
|
||||||
throw new IllegalArgumentException("Поле nationality класса Person не может быть null");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLocation(Location location) {
|
|
||||||
this.location = location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Статический метод, возвращающий список объектов Person,
|
|
||||||
* образованный из найденных в строке совпадений с
|
|
||||||
* регулярным выражением {@link #PERSON_REGEX PERSON_REGEX}
|
|
||||||
*
|
|
||||||
* @param str Строка, из которой требуется получить список людей
|
|
||||||
* @return Список объектов класса Person
|
|
||||||
*/
|
|
||||||
public static List<Person> peopleFromString(String str) {
|
|
||||||
List<Person> list = new ArrayList<>();
|
|
||||||
Matcher matcher = PERSON_REGEX.matcher(str);
|
|
||||||
while (matcher.find()) {
|
|
||||||
try {
|
|
||||||
Person person = new Person();
|
|
||||||
person.id = Long.parseLong(matcher.group("id"));
|
|
||||||
person.name = matcher.group("name");
|
|
||||||
person.height = Integer.parseInt(matcher.group("height"));
|
|
||||||
person.passportID = matcher.group("passportID");
|
|
||||||
person.eyeColor = Color.valueOf(matcher.group("eyeColor"));
|
|
||||||
person.nationality = Country.valueOf(matcher.group("nationality"));
|
|
||||||
person.coordinates = new Coordinates(
|
|
||||||
Float.parseFloat(matcher.group("coordX")),
|
|
||||||
Float.parseFloat(matcher.group("coordY"))
|
|
||||||
);
|
|
||||||
person.location = new Location(
|
|
||||||
Double.parseDouble(matcher.group("locX")),
|
|
||||||
Float.parseFloat(matcher.group("locY")),
|
|
||||||
Long.parseLong(matcher.group("locZ")),
|
|
||||||
matcher.group("locName")
|
|
||||||
);
|
|
||||||
String[] date = matcher.group("creationDate").split("[\\-.]");
|
|
||||||
person.creationDate = LocalDate.of(
|
|
||||||
Integer.parseInt(date[0]),
|
|
||||||
Integer.parseInt(date[1]),
|
|
||||||
Integer.parseInt(date[2])
|
|
||||||
);
|
|
||||||
list.add(person);
|
|
||||||
} catch (NumberFormatException | DateTimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.err.println("Что-то пошло не так при чтении данных из файла, не меняйте вручную данные в файле!!!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Переопределенный метод сравнения двух людей,
|
|
||||||
* сравнение производится по имени, номеру пасспорта,
|
|
||||||
* росту, национальности, местоположению и цвету глаз
|
|
||||||
*
|
|
||||||
* @param other Объект для сравнения
|
|
||||||
* @return Целое число - результат сравнения
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int compareTo(Person other) {
|
|
||||||
return Comparator.comparing(Person::getName)
|
|
||||||
.thenComparing(Person::getPassportID)
|
|
||||||
.thenComparing(Person::getHeight)
|
|
||||||
.thenComparing(Person::getNationality)
|
|
||||||
.thenComparing(Person::getLocation)
|
|
||||||
.thenComparing(Person::getEyeColor)
|
|
||||||
.compare(this, other);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
if (this == other) return true;
|
|
||||||
if (other == null || getClass() != other.getClass()) return false;
|
|
||||||
|
|
||||||
Person person = (Person) other;
|
|
||||||
|
|
||||||
if (!name.equals(person.name)) return false;
|
|
||||||
if (!coordinates.equals(person.coordinates)) return false;
|
|
||||||
if (!Objects.equals(height, person.height)) return false;
|
|
||||||
if (!Objects.equals(passportID, person.passportID)) return false;
|
|
||||||
if (eyeColor != person.eyeColor) return false;
|
|
||||||
if (nationality != person.nationality) return false;
|
|
||||||
return Objects.equals(location, person.location);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
int result = name.hashCode();
|
|
||||||
result = 31 * result + coordinates.hashCode();
|
|
||||||
result = 31 * result + (height != null ? height.hashCode() : 0);
|
|
||||||
result = 31 * result + (passportID != null ? passportID.hashCode() : 0);
|
|
||||||
result = 31 * result + eyeColor.hashCode();
|
|
||||||
result = 31 * result + nationality.hashCode();
|
|
||||||
result = 31 * result + (location != null ? location.hashCode() : 0);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format(Locale.US, "Person(id=%d, name=%s, coordinates=%s, creationDate=%s, height=%s, passportID=%s, " +
|
|
||||||
"eyeColor=%s, nationality=%s, location=%s)", this.id, this.name, this.coordinates, this.creationDate,
|
|
||||||
this.height, this.passportID, this.eyeColor, this.nationality, this.location);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,295 +0,0 @@
|
||||||
package ru.erius.lab5.collection;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Коллекция PersonTreeSet, использующая в основе своей работы коллекцию {@link TreeSet TreeSet},
|
|
||||||
* имеет возможности записи коллекции в файл и чтения её же из файла
|
|
||||||
*/
|
|
||||||
public class PersonTreeSet {
|
|
||||||
/**
|
|
||||||
* Название переменной окружения, где должен быть указан путь к файлу
|
|
||||||
*/
|
|
||||||
private final static String ENV_VAR = "PTS_PATH";
|
|
||||||
/**
|
|
||||||
* Тип коллекции, используется для вывода информации
|
|
||||||
*/
|
|
||||||
private final static String TYPE = "TreeSet";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Файл для записи и чтения коллекции, является null если путь к файлу некорректный
|
|
||||||
*/
|
|
||||||
private final File file = initFile();
|
|
||||||
/**
|
|
||||||
* Логические переменные, определяющие возможность программы записывать в файл или читать из него
|
|
||||||
*/
|
|
||||||
private boolean canWrite = true, canRead = true;
|
|
||||||
/**
|
|
||||||
* Коллекция {@link TreeSet TreeSet}, лежащая в основе работы класса
|
|
||||||
*/
|
|
||||||
private final TreeSet<Person> set = new TreeSet<>();
|
|
||||||
/**
|
|
||||||
* Дата инициализации коллекции
|
|
||||||
*/
|
|
||||||
private final LocalDate creationDate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Конструктор без параметров, инициализирует коллекцию из файла (если есть возможность)
|
|
||||||
* и задаёт дату инициализации
|
|
||||||
*/
|
|
||||||
public PersonTreeSet() {
|
|
||||||
this.creationDate = LocalDate.now();
|
|
||||||
if (this.file != null && this.canRead)
|
|
||||||
this.initTreeSet();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, возвращающий файл для записи и чтения коллекции,
|
|
||||||
* возвращает null если переменной окружении не существует,
|
|
||||||
* файла не существует, файл является директорией
|
|
||||||
* или имеет расширение, не являющееся .xml
|
|
||||||
*
|
|
||||||
* Устанавливает соответствующим полям {@link #canRead canRead} и {@link #canWrite canWrite} значения false,
|
|
||||||
* если нет прав на чтение или запись
|
|
||||||
*
|
|
||||||
* @return Файл, если все условия соблюдены, иначе null
|
|
||||||
*/
|
|
||||||
public File initFile() {
|
|
||||||
String path = System.getenv(ENV_VAR);
|
|
||||||
if (path == null) {
|
|
||||||
System.err.println("Переменная окружения " + ENV_VAR + " (путь к файлу) не задана, возможность сохранения и загрузки коллекции в файл отключена");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
File file = new File(path);
|
|
||||||
if (!file.exists()) {
|
|
||||||
System.err.println("Файла по пути " + path + " не существует, возможность сохранения и загрузки коллекции в файл отключена");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.isFile()) {
|
|
||||||
System.err.println("В " + ENV_VAR + " записан путь к директории, возможность сохранения и загрузки коллекции в файл отключена");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String ext = this.getFileExtension(file.getPath());
|
|
||||||
if (ext == null || !ext.equals("xml")) {
|
|
||||||
System.err.println("Файл должен иметь расширение xml, возможность сохранения и загрузки коллекции в файл отключена");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.canWrite()) {
|
|
||||||
System.err.println("Запись в этот файл невозможна, возможность сохранения коллекции файл отключена");
|
|
||||||
this.canWrite = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!file.canRead()) {
|
|
||||||
System.err.println("Чтение из этого файл невозможно, возможность загрузки коллекции из файла отключена");
|
|
||||||
this.canRead = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Файл для сохранения и загрузки коллекции был успешно найден");
|
|
||||||
System.out.println("Путь: " + path);
|
|
||||||
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод инициализации коллекции из файла,
|
|
||||||
* читает содержимое файла и передаёт его в метод {@link Person#peopleFromString(String)},
|
|
||||||
* элементы полученного списка добавляются в коллекцию
|
|
||||||
*/
|
|
||||||
private void initTreeSet() {
|
|
||||||
StringBuilder fileContents = new StringBuilder();
|
|
||||||
try (InputStreamReader reader = new InputStreamReader(new FileInputStream(this.file))) {
|
|
||||||
int read;
|
|
||||||
do {
|
|
||||||
char[] buffer = new char[8192];
|
|
||||||
read = reader.read(buffer);
|
|
||||||
fileContents.append(buffer);
|
|
||||||
} while (read != -1);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
System.err.println("Не удалось инициализировать коллекцию из файла");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String contents = fileContents.toString();
|
|
||||||
List<Person> people = Person.peopleFromString(contents);
|
|
||||||
this.set.addAll(people);
|
|
||||||
System.out.println("Коллекция успешно инициализирована из файла");
|
|
||||||
}
|
|
||||||
|
|
||||||
public File getFile() {
|
|
||||||
return file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, возвращающий расширение файла
|
|
||||||
*
|
|
||||||
* @param path Строка, в которой записан путь к файлу
|
|
||||||
*
|
|
||||||
* @return Расширение файла или null, если путь к файлу некорректный
|
|
||||||
*/
|
|
||||||
private String getFileExtension(String path) {
|
|
||||||
String[] split = path.split("\\.", 2);
|
|
||||||
return split.length > 1 ? split[1] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, возвращающий строку с информацией о коллекции
|
|
||||||
*
|
|
||||||
* @return Строка с информацией о коллекции
|
|
||||||
*/
|
|
||||||
public String info() {
|
|
||||||
return String.format("Тип коллекции: %s \n" +
|
|
||||||
"Дата инициализации: %s \n" +
|
|
||||||
"Количество элементов: %d \n" +
|
|
||||||
"Путь к файлу хранения: %s",
|
|
||||||
PersonTreeSet.TYPE, this.creationDate, this.set.size(), this.file);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод добавления нового элемента в коллекцию
|
|
||||||
*
|
|
||||||
* @param person Элемент для добавления
|
|
||||||
*
|
|
||||||
* @return true, если элемент был успешно добавлен, иначе false
|
|
||||||
*/
|
|
||||||
public boolean add(Person person) {
|
|
||||||
return this.set.add(person);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод изменения элемента в коллекции по заданному id
|
|
||||||
*
|
|
||||||
* @param id Id человека, которого нужно изменить
|
|
||||||
* @param person Элемент для добавления
|
|
||||||
*
|
|
||||||
* @return true, если элемент был успешно найден и изменён, иначе false
|
|
||||||
*/
|
|
||||||
public boolean update(long id, Person person) {
|
|
||||||
Optional<Person> element = this.set.stream().filter(p -> p.getId() == id).findAny();
|
|
||||||
if (element.isEmpty()) return false;
|
|
||||||
Person p = element.get();
|
|
||||||
p.setName(person.getName());
|
|
||||||
p.setCoordinates(person.getCoordinates());
|
|
||||||
p.setHeight(person.getHeight());
|
|
||||||
p.setPassportID(person.getPassportID());
|
|
||||||
p.setEyeColor(person.getEyeColor());
|
|
||||||
p.setNationality(person.getNationality());
|
|
||||||
p.setLocation(person.getLocation());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод удаления элемента из коллекции по заданному id
|
|
||||||
*
|
|
||||||
* @param id Id человека, которого нужно удалить
|
|
||||||
*
|
|
||||||
* @return true, если элемент был успешно найден и удален, иначе false
|
|
||||||
*/
|
|
||||||
public boolean remove(long id) {
|
|
||||||
return this.set.removeIf(p -> p.getId() == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод очистки коллекции
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
this.set.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод сохранения коллекции в файл
|
|
||||||
*
|
|
||||||
* @return true, если коллекция была успешно сохранена, иначе false
|
|
||||||
*/
|
|
||||||
public boolean save() {
|
|
||||||
if (file == null || !this.canWrite)
|
|
||||||
return false;
|
|
||||||
try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(file))) {
|
|
||||||
writer.write(this.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод добавления элемента в коллекцию, если он больше её максимального элемента
|
|
||||||
*
|
|
||||||
* @param person Элемент для добавления
|
|
||||||
*
|
|
||||||
* @return true, если элемент был успешно добавлен, иначе false
|
|
||||||
*/
|
|
||||||
public boolean addIfMax(Person person) {
|
|
||||||
Person last = this.set.last();
|
|
||||||
if (person.compareTo(last) > 0)
|
|
||||||
return this.set.add(person);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод добавления элемента в коллекцию, если он меньше её минимального элемента
|
|
||||||
*
|
|
||||||
* @param person Элемент для добавления
|
|
||||||
*
|
|
||||||
* @return true, если элемент был успешно добавлен, иначе false
|
|
||||||
*/
|
|
||||||
public boolean addIfMin(Person person) {
|
|
||||||
Person first = this.set.first();
|
|
||||||
if (person.compareTo(first) < 0)
|
|
||||||
return this.set.add(person);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, складывающий рост каждого человека в коллекции
|
|
||||||
*
|
|
||||||
* @return Суммарный рост всех людей
|
|
||||||
*/
|
|
||||||
public int sumOfHeight() {
|
|
||||||
return this.set.stream()
|
|
||||||
.mapToInt(Person::getHeight)
|
|
||||||
.sum();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, фильтрующий всех людей, в имени которых имеется строка name,
|
|
||||||
* проверка не чувствительна к регистру
|
|
||||||
*
|
|
||||||
* @param name Строка, по которой происходит фильтрация
|
|
||||||
*
|
|
||||||
* @return Список людей, в имени которых имеется строка name
|
|
||||||
*/
|
|
||||||
public List<Person> filterContainsName(String name) {
|
|
||||||
return this.set.stream()
|
|
||||||
.filter(p -> p.getName().toLowerCase(Locale.ROOT).contains(name.toLowerCase(Locale.ROOT)))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Метод, возвращающий отсортированный по убыванию список местоположений всех людей в коллекции
|
|
||||||
*
|
|
||||||
* @return Отсортированный по убыванию список местоположений
|
|
||||||
*/
|
|
||||||
public List<Location> fieldDescendingLocation() {
|
|
||||||
return this.set.stream()
|
|
||||||
.map(Person::getLocation)
|
|
||||||
.sorted(Collections.reverseOrder())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "PersonTreeSet(\n" +
|
|
||||||
this.set.stream()
|
|
||||||
.map(p -> "\t" + p.toString())
|
|
||||||
.collect(Collectors.joining(",\n")) +
|
|
||||||
"\n)";
|
|
||||||
}
|
|
||||||
}
|
|
10
src/main/java/ru/erius/lab5/commandline/Command.java
Normal file
10
src/main/java/ru/erius/lab5/commandline/Command.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package ru.erius.lab5.commandline;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Функциональный интерфейс с одним методом {@link #execute(String[])},
|
||||||
|
* используется для реализации шаблон проектирования Command
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface Command {
|
||||||
|
void execute(String[] args);
|
||||||
|
}
|
223
src/main/java/ru/erius/lab5/commandline/CommandLineHandler.java
Normal file
223
src/main/java/ru/erius/lab5/commandline/CommandLineHandler.java
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
package ru.erius.lab5.commandline;
|
||||||
|
|
||||||
|
import ru.erius.lab5.util.UtilFunctions;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс обработчика командной строки, реализует шаблон проектирования Singleton,
|
||||||
|
* читает ввод с командной строки, обрабатывает его и вызывает соответствующую команду
|
||||||
|
* на выполнение из регистра команд, используйте метод {@link #start()} для его запуска
|
||||||
|
*
|
||||||
|
* @see CommandRegistry
|
||||||
|
*/
|
||||||
|
public final class CommandLineHandler {
|
||||||
|
|
||||||
|
private final static CommandLineHandler instance = new CommandLineHandler();
|
||||||
|
|
||||||
|
private final Deque<Reader> inputs = new LinkedList<>();
|
||||||
|
private final List<String> history = new LinkedList<>();
|
||||||
|
private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||||
|
private boolean isActive = false;
|
||||||
|
|
||||||
|
static {
|
||||||
|
clearScreen();
|
||||||
|
CommandLineHandler.registerBasicCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandLineHandler() {}
|
||||||
|
|
||||||
|
public static CommandLineHandler getInstance() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerBasicCommands() {
|
||||||
|
CommandRegistry.registerCommand("exit", args -> instance.exit(),
|
||||||
|
"exit : завершить программу (без сохранения в файл)");
|
||||||
|
CommandRegistry.registerCommand("history", instance::showHistory,
|
||||||
|
"history [count] : вывести последние count введенных команд, по умолчанию count равен 6");
|
||||||
|
CommandRegistry.registerCommand("execute_script", instance::executeScript,
|
||||||
|
"execute_script {file_name} : считать и исполнить скрипт из указанного файла. В скрипте содержатся команды в таком же виде, в котором их вводит пользователь в интерактивном режиме.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, запускающий обработчик командной строки, для остановки требуется ввести команду "exit"
|
||||||
|
*/
|
||||||
|
public void start() {
|
||||||
|
System.out.println(LongStrings.GREETINGS.getValue());
|
||||||
|
this.isActive = true;
|
||||||
|
do {
|
||||||
|
input();
|
||||||
|
} while (this.isActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void clearScreen() {
|
||||||
|
try {
|
||||||
|
if (System.getProperty("os.name").contains("Windows"))
|
||||||
|
new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
|
||||||
|
else
|
||||||
|
Runtime.getRuntime().exec("clear");
|
||||||
|
} catch (IOException | InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void input() {
|
||||||
|
String line = awaitInput("lab5>", "Что-то пошло не так").toLowerCase(Locale.ROOT);
|
||||||
|
String[] split = line.split("\\s+");
|
||||||
|
String alias = split[0];
|
||||||
|
String[] args = new String[]{};
|
||||||
|
if (split.length > 1) {
|
||||||
|
args = new String[split.length - 1];
|
||||||
|
System.arraycopy(split, 1, args, 0, split.length - 1);
|
||||||
|
}
|
||||||
|
Command command = CommandRegistry.getCommand(alias);
|
||||||
|
if (command == null) {
|
||||||
|
System.out.println("Неизвестная команда " + alias + ", напишите help для отображения всех существующих команд");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
command.execute(args);
|
||||||
|
updateHistory(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, останавливающий работу обработчика командной строки
|
||||||
|
*/
|
||||||
|
public void exit() {
|
||||||
|
this.isActive = false;
|
||||||
|
System.out.println("Выход из программы...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void showHistory(String[] args) {
|
||||||
|
int lines = 6;
|
||||||
|
if (args.length >= 1 && UtilFunctions.intOrNull(args[0]) != null)
|
||||||
|
lines = Integer.parseInt(args[0]);
|
||||||
|
if (lines <= 0) {
|
||||||
|
System.out.println("[count] должен быть больше 0");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int start = lines < history.size() ? history.size() - lines : 0;
|
||||||
|
System.out.println("История последних команд:");
|
||||||
|
for (int i = start; i < history.size(); i++)
|
||||||
|
System.out.println(history.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void executeScript(String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
System.out.println("Недостаточно данных");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileName = args[0];
|
||||||
|
File file = new File(fileName);
|
||||||
|
if (!file.exists() || file.isDirectory()) {
|
||||||
|
System.out.println("Не удалось найти файл " + fileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reader streamReader;
|
||||||
|
try {
|
||||||
|
streamReader = new InputStreamReader(new FileInputStream(file));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.out.println("Что-то пошло не так");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
addNewInput(streamReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateHistory(String command) {
|
||||||
|
history.add(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, ожидающий ввода из потока ввода {@link #reader reader} и возвращающий результат,
|
||||||
|
* печатает запрос msg перед ожиданием данных (если их вводит пользователь),
|
||||||
|
* печатает ошибку err, если при вводе данных произошла ошибка
|
||||||
|
*
|
||||||
|
* @param msg Строка, печатающаяся как запрос данных от пользователя
|
||||||
|
* @param err Строка, печатающаяся во время ошибки
|
||||||
|
*
|
||||||
|
* @return Строка из потока ввода
|
||||||
|
*/
|
||||||
|
public String awaitInput(String msg, String err) {
|
||||||
|
return awaitInput(msg, err, input -> true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, ожидающий ввода из потока ввода {@link #reader reader } и возвращающий результат,
|
||||||
|
* печатает запрос msg перед ожиданием данных (если их вводит пользователь),
|
||||||
|
* печатает ошибку err, если введенные данные не соответствуют предикату predicate
|
||||||
|
*
|
||||||
|
* @param msg Строка, печатающаяся как запрос данных от пользователя
|
||||||
|
* @param err Строка, печатающаяся при несоответствии ввода предикату
|
||||||
|
* @param predicate Предикат, определяющий валидность введенных данных
|
||||||
|
*
|
||||||
|
* @return Строка из потока ввода
|
||||||
|
*/
|
||||||
|
public String awaitInput(String msg, String err, Predicate<String> predicate) {
|
||||||
|
String input = null;
|
||||||
|
do {
|
||||||
|
if (inputs.isEmpty())
|
||||||
|
System.out.print(msg + " ");
|
||||||
|
try {
|
||||||
|
input = reader.readLine();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if (input == null) {
|
||||||
|
removeInput();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
input = input.trim();
|
||||||
|
if (predicate.test(input))
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
System.err.println(err);
|
||||||
|
} while (true);
|
||||||
|
System.out.println();
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, ожидающий ввода из потока ввода {@link #reader reader} и возвращающий результат,
|
||||||
|
* печатает запрос msg перед ожиданием данных (если их вводит пользователь),
|
||||||
|
* печатает ошибку err, если введенные данные не соответствуют предикату predicate,
|
||||||
|
* преобразует результат в тип T в соответствии с функцией transform
|
||||||
|
*
|
||||||
|
* @param msg Строка, печатающаяся как запрос данных от пользователя
|
||||||
|
* @param err Строка, печатающаяся при несоответствии ввода предикату
|
||||||
|
* @param predicate Предикат, определяющий валидность введенных данных
|
||||||
|
* @param <T> Тип, к которому будет приведен результат
|
||||||
|
* @param transform Функция, преобразующая результат в тип T
|
||||||
|
*
|
||||||
|
* @return Результат типа T
|
||||||
|
*/
|
||||||
|
public <T> T awaitInput(String msg, String err, Predicate<String> predicate, Function<String, T> transform) {
|
||||||
|
String result = awaitInput(msg, err, predicate);
|
||||||
|
return transform.apply(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, меняющий текущий поток ввода на stream и добавляет его в очередь {@link #inputs inputs}
|
||||||
|
*
|
||||||
|
* @param reader Новый поток ввода
|
||||||
|
*/
|
||||||
|
public void addNewInput(Reader reader) {
|
||||||
|
this.reader = new BufferedReader(reader);
|
||||||
|
this.inputs.add(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, убирающий текущий поток ввода из очереди {@link #inputs inputs}
|
||||||
|
* и меняющий его либо на следующий в очереди поток, либо на System.in, если очередь пуста
|
||||||
|
*/
|
||||||
|
public void removeInput() {
|
||||||
|
inputs.poll();
|
||||||
|
Reader reader = inputs.isEmpty() ? new InputStreamReader(System.in) : inputs.peek();
|
||||||
|
this.reader = new BufferedReader(reader);
|
||||||
|
}
|
||||||
|
}
|
85
src/main/java/ru/erius/lab5/commandline/CommandRegistry.java
Normal file
85
src/main/java/ru/erius/lab5/commandline/CommandRegistry.java
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package ru.erius.lab5.commandline;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс регистра команд, используемый для добавления, переопределения или удаления команд
|
||||||
|
*/
|
||||||
|
public final class CommandRegistry {
|
||||||
|
|
||||||
|
private static final Map<String, DescriptiveCommand> COMMANDS = new HashMap<>();
|
||||||
|
|
||||||
|
private CommandRegistry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
registerCommand("help", args -> help(),
|
||||||
|
"help : вывести справку по доступным командам");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerCommand(String alias, Command command) {
|
||||||
|
registerCommand(alias, command, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerCommand(String alias, Command command, String desc) {
|
||||||
|
if (COMMANDS.containsKey(alias))
|
||||||
|
throw new CommandAlreadyExistsException("Команда %s уже существует, " +
|
||||||
|
"используйте метод reassignCommand() для переопределения существующей команды", alias);
|
||||||
|
COMMANDS.put(alias, new DescriptiveCommand(command, desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void reassignCommand(String alias, Command command, String desc) {
|
||||||
|
if (!COMMANDS.containsKey(alias))
|
||||||
|
throw new CommandNotFoundException("Не удалось переопределить несуществующую команду %s", alias);
|
||||||
|
COMMANDS.put(alias, new DescriptiveCommand(command, desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void unregisterCommand(String alias) {
|
||||||
|
if (!COMMANDS.containsKey(alias))
|
||||||
|
throw new CommandNotFoundException("Не удалось удалить несуществующую команду %s", alias);
|
||||||
|
COMMANDS.remove(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Command getCommand(String alias) {
|
||||||
|
DescriptiveCommand command = COMMANDS.get(alias);
|
||||||
|
return command == null ? null : command.getCommand() ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void help() {
|
||||||
|
System.out.println(LongStrings.LINE.getValue() + "\n");
|
||||||
|
List<DescriptiveCommand> commands = COMMANDS.values().stream()
|
||||||
|
.sorted(Comparator.comparing(DescriptiveCommand::getDesc))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for (DescriptiveCommand dc : commands) {
|
||||||
|
String desc = dc.getDesc();
|
||||||
|
if (desc != null) System.out.println(desc + "\n");
|
||||||
|
}
|
||||||
|
System.out.println(LongStrings.LINE.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CommandNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
private CommandNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandNotFoundException(String message, String alias) {
|
||||||
|
super(String.format(message, alias));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CommandAlreadyExistsException extends RuntimeException {
|
||||||
|
|
||||||
|
private CommandAlreadyExistsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandAlreadyExistsException(String message, String alias) {
|
||||||
|
super(String.format(message, alias));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ru.erius.lab5.commandline;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NonNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс команд с описанием, оболочка для обычных команд, используются для корректной работы команды help
|
||||||
|
*/
|
||||||
|
@Data @AllArgsConstructor
|
||||||
|
public class DescriptiveCommand {
|
||||||
|
@NonNull
|
||||||
|
private final Command command;
|
||||||
|
private final String desc;
|
||||||
|
}
|
21
src/main/java/ru/erius/lab5/commandline/LongStrings.java
Normal file
21
src/main/java/ru/erius/lab5/commandline/LongStrings.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package ru.erius.lab5.commandline;
|
||||||
|
|
||||||
|
public enum LongStrings {
|
||||||
|
LINE("=================================================================================================="),
|
||||||
|
|
||||||
|
GREETINGS(LINE.value + "\n" +
|
||||||
|
"Добро пожаловать в программу для управления коллекцией объектов в интерактивном режиме!\n" +
|
||||||
|
"Напишите help, чтобы увидеть доступные команды\n" +
|
||||||
|
"Напишите exit, чтобы выйти из программы\n" +
|
||||||
|
LINE.value + "\n");
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
LongStrings(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,235 @@
|
||||||
|
package ru.erius.lab5.commandline;
|
||||||
|
|
||||||
|
import ru.erius.lab5.collection.Database;
|
||||||
|
import ru.erius.lab5.collection.PeopleDatabase;
|
||||||
|
import ru.erius.lab5.data.*;
|
||||||
|
import ru.erius.lab5.util.UtilFunctions;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static ru.erius.lab5.commandline.CommandRegistry.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс объявления всех команд, связанных с манипуляциями над объектом класса PeopleDatabase
|
||||||
|
*
|
||||||
|
* @see PeopleDatabase
|
||||||
|
*/
|
||||||
|
public final class PeopleDatabaseCommands {
|
||||||
|
|
||||||
|
private static PeopleDatabase peopleDatabase;
|
||||||
|
private static final String COLORS = Arrays.toString(Color.values()),
|
||||||
|
COUNTRIES = Arrays.toString(Country.values());
|
||||||
|
private static final CommandLineHandler CMD = CommandLineHandler.getInstance();
|
||||||
|
|
||||||
|
private PeopleDatabaseCommands() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод добавления всех команд в регистр команд, обязательно вызывайте его в своей программе,
|
||||||
|
* если вам требуются данные команды
|
||||||
|
*/
|
||||||
|
public static void registerDatabaseCommands() {
|
||||||
|
registerCommand("info", args -> info(peopleDatabase),
|
||||||
|
"info : вывести в стандартный поток вывода информацию о коллекции (тип, дата инициализации, количество элементов и т.д.)");
|
||||||
|
registerCommand("show", args -> show(peopleDatabase),
|
||||||
|
"show : вывести в стандартный поток вывода все элементы коллекции в строковом представлении");
|
||||||
|
registerCommand("add", args -> add(peopleDatabase),
|
||||||
|
"add <Person> : добавить новый элемент в коллекцию");
|
||||||
|
registerCommand("update", args -> update(peopleDatabase, args),
|
||||||
|
"update {id} <Person> : обновить значение элемента коллекции, {id} которого равен заданному");
|
||||||
|
registerCommand("remove_by_id", args -> remove(peopleDatabase, args),
|
||||||
|
"remove_by_id {id} : удалить элемент из коллекции по его {id}");
|
||||||
|
registerCommand("clear", args -> clear(peopleDatabase),
|
||||||
|
"clear : очистить коллекцию");
|
||||||
|
registerCommand("save", args -> save(peopleDatabase),
|
||||||
|
"save : сохранить коллекцию в файл");
|
||||||
|
registerCommand("add_if_max", args -> addIfMax(peopleDatabase),
|
||||||
|
"add_if_max <Person> : добавить новый элемент в коллекцию, если его значение превышает значение наибольшего элемента этой коллекции");
|
||||||
|
registerCommand("add_if_min", args -> addIfMin(peopleDatabase),
|
||||||
|
"add_if_min <Person> : добавить новый элемент в коллекцию, если его значение меньше, чем у наименьшего элемента этой коллекции");
|
||||||
|
registerCommand("sum_of_height", args -> sumOfHeight(peopleDatabase),
|
||||||
|
"sum_of_height : вывести сумму значений поля height для всех элементов коллекции");
|
||||||
|
registerCommand("filter_contains_name", args -> filterContainsName(peopleDatabase, args),
|
||||||
|
"filter_contains_name {name} : вывести элементы, значение поля name которых содержит заданную подстроку");
|
||||||
|
registerCommand("print_field_descending_location", args -> printFieldDescendingLocation(peopleDatabase),
|
||||||
|
"print_field_descending_location : вывести значения поля location всех элементов в порядке убывания");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPeopleDatabase(PeopleDatabase peopleDatabase) {
|
||||||
|
PeopleDatabaseCommands.peopleDatabase = peopleDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void info(PeopleDatabase peopleDatabase) {
|
||||||
|
System.out.println(peopleDatabase.info());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(PeopleDatabase peopleDatabase) {
|
||||||
|
System.out.println(peopleDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void add(PeopleDatabase peopleDatabase) {
|
||||||
|
Person person = createPerson();
|
||||||
|
boolean success = peopleDatabase.getCollection().add(person);
|
||||||
|
System.out.println(success ? "Человек успешно добавлен в коллекцию" : "Не удалось добавить человека в коллекцию");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void update(PeopleDatabase peopleDatabase, String[] args) {
|
||||||
|
Long id = getIdOrNull(args);
|
||||||
|
if (id == null) return;
|
||||||
|
|
||||||
|
Optional<Person> optionalPerson = peopleDatabase.getCollection()
|
||||||
|
.stream()
|
||||||
|
.filter(p -> p.getId().equals(id))
|
||||||
|
.findFirst();
|
||||||
|
if (!optionalPerson.isPresent()) {
|
||||||
|
System.out.println("Человек с {id} " + id + " не был найден");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Person oldPerson = optionalPerson.get();
|
||||||
|
peopleDatabase.getCollection().remove(oldPerson);
|
||||||
|
|
||||||
|
Person newPerson = createPerson();
|
||||||
|
oldPerson.update(newPerson);
|
||||||
|
peopleDatabase.getCollection().add(oldPerson);
|
||||||
|
System.out.println("Человек с {id} " + oldPerson.getId() + " был успешно изменен");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void remove(PeopleDatabase peopleDatabase, String[] args) {
|
||||||
|
Long id = getIdOrNull(args);
|
||||||
|
if (id == null) return;
|
||||||
|
boolean success = peopleDatabase.getCollection().removeIf(p -> p.getId().equals(id));
|
||||||
|
System.out.println("Человек с {id} " + id + " " + (success ? "был успешно удален" : "не был найден"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear(PeopleDatabase peopleDatabase) {
|
||||||
|
peopleDatabase.getCollection().clear();
|
||||||
|
System.out.println("Коллекция была очищена");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void save(PeopleDatabase peopleDatabase) {
|
||||||
|
try {
|
||||||
|
peopleDatabase.save();
|
||||||
|
System.out.println("Коллекция была успешно сохранена");
|
||||||
|
} catch (Database.DatabaseSaveFailedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
System.out.println("Не удалось сохранить коллекцию");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addIfMax(PeopleDatabase peopleDatabase) {
|
||||||
|
Person person = createPerson();
|
||||||
|
Person last = peopleDatabase.getCollection().last();
|
||||||
|
if (person.compareTo(last) > 0) {
|
||||||
|
peopleDatabase.getCollection().add(person);
|
||||||
|
System.out.println("Человек успешно добавлен в коллекцию");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println("Не удалось добавить человека в коллекцию");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addIfMin(PeopleDatabase peopleDatabase) {
|
||||||
|
Person person = createPerson();
|
||||||
|
Person first = peopleDatabase.getCollection().first();
|
||||||
|
if (person.compareTo(first) < 0) {
|
||||||
|
peopleDatabase.getCollection().add(person);
|
||||||
|
System.out.println("Человек успешно добавлен в коллекцию");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println("Не удалось добавить человека в коллекцию");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sumOfHeight(PeopleDatabase peopleDatabase) {
|
||||||
|
int sum = peopleDatabase.getCollection()
|
||||||
|
.stream()
|
||||||
|
.mapToInt(Person::getHeight)
|
||||||
|
.sum();
|
||||||
|
System.out.println("Сумма ростов всех людей в коллекции - " + sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void filterContainsName(PeopleDatabase peopleDatabase, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
System.out.println("Недостаточно данных");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = args[0];
|
||||||
|
System.out.println("Список людей, в имени которых содержится " + name + ":");
|
||||||
|
peopleDatabase.getCollection()
|
||||||
|
.stream()
|
||||||
|
.filter(p -> p.getName().contains(name))
|
||||||
|
.forEach(System.out::println);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printFieldDescendingLocation(PeopleDatabase peopleDatabase) {
|
||||||
|
System.out.println("Список локаций в порядке убывания");
|
||||||
|
peopleDatabase.getCollection()
|
||||||
|
.stream()
|
||||||
|
.map(Person::getLocation)
|
||||||
|
.sorted(Collections.reverseOrder())
|
||||||
|
.forEach(System.out::println);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Long getIdOrNull(String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
System.out.println("Недостаточно данных");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long id = UtilFunctions.longOrNull(args[0]);
|
||||||
|
if (id == null) {
|
||||||
|
System.out.println("{id} должен быть целым числом");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Person createPerson() {
|
||||||
|
System.out.println("Создание нового объекта класса Person");
|
||||||
|
String name = CMD.awaitInput("Введите имя:", "Введите непустую строку",
|
||||||
|
input -> !input.isEmpty());
|
||||||
|
int height = CMD.awaitInput("Введите рост:", "Введите целое число, большее нуля",
|
||||||
|
input -> {
|
||||||
|
Integer result = UtilFunctions.intOrNull(input);
|
||||||
|
return result != null && result > 0;
|
||||||
|
}, Integer::parseInt);
|
||||||
|
String passportID = CMD.awaitInput("Введите номер паспорта:", "Введите минимум 8 символов",
|
||||||
|
input -> input.length() >= 8);
|
||||||
|
Color eyeColor = CMD.awaitInput("Введите цвет глаз " + COLORS + ":", "Введите один из предложенных цветов",
|
||||||
|
input -> UtilFunctions.enumOrNull(input.toUpperCase(Locale.ROOT), Color.class) != null,
|
||||||
|
input -> Color.valueOf(input.toUpperCase(Locale.ROOT)));
|
||||||
|
Country nationality = CMD.awaitInput("Введите национальность " + COUNTRIES + ":", "Введите одну из предложенных стран",
|
||||||
|
input -> UtilFunctions.enumOrNull(input.toUpperCase(Locale.ROOT), Country.class) != null,
|
||||||
|
input -> Country.valueOf(input.toUpperCase(Locale.ROOT)));
|
||||||
|
Location location = createLocation();
|
||||||
|
Coordinates coordinates = createCoordinates();
|
||||||
|
return new Person(name, coordinates, height, passportID, eyeColor, nationality, location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Location createLocation() {
|
||||||
|
System.out.println("Создание нового объекта класса Location");
|
||||||
|
double x = CMD.awaitInput("Введите x:", "Введите дробное число",
|
||||||
|
input -> UtilFunctions.doubleOrNull(input) != null, Double::parseDouble);
|
||||||
|
float y = CMD.awaitInput("Введите y:", "Введите дробное число",
|
||||||
|
input -> UtilFunctions.floatOrNull(input) != null, Float::parseFloat);
|
||||||
|
long z = CMD.awaitInput("Введите z:", "Введите целое число",
|
||||||
|
input -> UtilFunctions.longOrNull(input) != null, Long::parseLong);
|
||||||
|
String name = CMD.awaitInput("Введите название:", "Строка не может быть пустой",
|
||||||
|
input -> !input.isEmpty());
|
||||||
|
return new Location(x, y, z, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Coordinates createCoordinates() {
|
||||||
|
System.out.println("Создание нового объекта класса Coordinates");
|
||||||
|
float x = CMD.awaitInput("Введите x:", "Введите дробное число",
|
||||||
|
input -> UtilFunctions.floatOrNull(input) != null, Float::parseFloat);
|
||||||
|
float y = CMD.awaitInput("Введите y:", "Введите дробное число, большее -816",
|
||||||
|
input -> {
|
||||||
|
Float result = UtilFunctions.floatOrNull(input);
|
||||||
|
return result != null && result > -816F;
|
||||||
|
}, Float::parseFloat);
|
||||||
|
return new Coordinates(x, y);
|
||||||
|
}
|
||||||
|
}
|
10
src/main/java/ru/erius/lab5/data/Color.java
Normal file
10
src/main/java/ru/erius/lab5/data/Color.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package ru.erius.lab5.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перечисление цветов
|
||||||
|
*/
|
||||||
|
public enum Color {
|
||||||
|
BLACK,
|
||||||
|
ORANGE,
|
||||||
|
BROWN
|
||||||
|
}
|
48
src/main/java/ru/erius/lab5/data/Coordinates.java
Normal file
48
src/main/java/ru/erius/lab5/data/Coordinates.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package ru.erius.lab5.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс данных координат
|
||||||
|
*/
|
||||||
|
@Data @NoArgsConstructor @EqualsAndHashCode @ToString
|
||||||
|
public class Coordinates {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Координата X типа float
|
||||||
|
*/
|
||||||
|
private float x;
|
||||||
|
/**
|
||||||
|
* Координата Y типа float, значение должно быть больше -816
|
||||||
|
*/
|
||||||
|
private float y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструктор с параметрами
|
||||||
|
*
|
||||||
|
* @param x Координата X
|
||||||
|
* @param y Координата Y
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Если Y меньше или равен -816
|
||||||
|
*/
|
||||||
|
public Coordinates(float x, float y) {
|
||||||
|
this.x = x;
|
||||||
|
this.setY(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сеттер для поля y
|
||||||
|
*
|
||||||
|
* @param y Координата Y
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Если Y меньше или равен -816
|
||||||
|
*/
|
||||||
|
public void setY(float y) {
|
||||||
|
this.y = y;
|
||||||
|
if (y <= -816)
|
||||||
|
throw new IllegalArgumentException("Поле y класса Coordinates должно быть больше -816");
|
||||||
|
}
|
||||||
|
}
|
12
src/main/java/ru/erius/lab5/data/Country.java
Normal file
12
src/main/java/ru/erius/lab5/data/Country.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package ru.erius.lab5.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Перечисление стран
|
||||||
|
*/
|
||||||
|
public enum Country {
|
||||||
|
UNITED_KINGDOM,
|
||||||
|
GERMANY,
|
||||||
|
CHINA,
|
||||||
|
THAILAND,
|
||||||
|
JAPAN
|
||||||
|
}
|
84
src/main/java/ru/erius/lab5/data/Location.java
Normal file
84
src/main/java/ru/erius/lab5/data/Location.java
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package ru.erius.lab5.data;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс данных местоположения, реализует сортировку по умолчанию
|
||||||
|
* по имени и расстоянию до точки (0; 0; 0)
|
||||||
|
*/
|
||||||
|
@Data @NoArgsConstructor @EqualsAndHashCode @ToString
|
||||||
|
public class Location implements Comparable<Location> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Координата X типа double
|
||||||
|
*/
|
||||||
|
private double x;
|
||||||
|
/**
|
||||||
|
* Координата Y типа float
|
||||||
|
*/
|
||||||
|
private float y;
|
||||||
|
/**
|
||||||
|
* Координата Z типа Long, не может быть null
|
||||||
|
*/
|
||||||
|
private Long z;
|
||||||
|
/**
|
||||||
|
* Имя локации, может быть null
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструктор с параметрами
|
||||||
|
*
|
||||||
|
* @param x Координата X
|
||||||
|
* @param y Координата Y
|
||||||
|
* @param z Координата Z
|
||||||
|
* @param name Имя локации
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException будет брошено, если name является пустой строкой
|
||||||
|
*
|
||||||
|
* @throws NullPointerException будет брошено в случае, если Z является null
|
||||||
|
*/
|
||||||
|
public Location(double x, float y, @NonNull Long z, String name) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.setName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сеттер для поля name
|
||||||
|
* @param name Имя локации
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Если name является пустой строкой
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
if (name.isEmpty())
|
||||||
|
throw new IllegalArgumentException("Поле name класса Location не может быть пустым");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переопределенный метод сравнения двух местоположений,
|
||||||
|
* сравнение производится по имени локации и расстоянию до точки (0; 0; 0)
|
||||||
|
*
|
||||||
|
* @param other Объект для сравнения
|
||||||
|
* @return Целое число - результат сравнения
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compareTo(Location other) {
|
||||||
|
return Comparator.comparing((Location loc) -> loc.name)
|
||||||
|
.thenComparing(Location::distance)
|
||||||
|
.compare(this, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, вычисляющий расстояние до точки (0; 0; 0)
|
||||||
|
*
|
||||||
|
* @return Расстояние типа double
|
||||||
|
*/
|
||||||
|
private double distance() {
|
||||||
|
return Math.sqrt(x * x + y * y + z * z);
|
||||||
|
}
|
||||||
|
}
|
182
src/main/java/ru/erius/lab5/data/Person.java
Normal file
182
src/main/java/ru/erius/lab5/data/Person.java
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
package ru.erius.lab5.data;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NonNull;
|
||||||
|
import lombok.ToString;
|
||||||
|
import ru.erius.lab5.parser.LocalDateAdapter;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.*;
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс данных человека, реализует сортировку по умолчанию по имени, номеру паспорта,
|
||||||
|
* росту, национальности, местоположению и цвету глаз
|
||||||
|
*/
|
||||||
|
@Data @EqualsAndHashCode @ToString
|
||||||
|
@XmlRootElement
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
public class Person implements Comparable<Person> {
|
||||||
|
/**
|
||||||
|
* Количество созданных людей, используется для задания
|
||||||
|
* уникального id для каждого объекта данного класса
|
||||||
|
*/
|
||||||
|
@XmlTransient
|
||||||
|
private static long existingPeople = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Id человека, не может быть null, значение поля должно быть больше 0,
|
||||||
|
* значение этого поля должно быть уникальным, значение этого поля должно генерироваться автоматически
|
||||||
|
*/
|
||||||
|
@XmlTransient
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* Имя человека, не может быть null, строка не может быть пустой
|
||||||
|
*/
|
||||||
|
private String name;
|
||||||
|
/**
|
||||||
|
* Координаты человека, не может быть null
|
||||||
|
*/
|
||||||
|
private Coordinates coordinates;
|
||||||
|
/**
|
||||||
|
* Дата создания объекта, не может быть null, значение этого поля должно генерироваться автоматически
|
||||||
|
*/
|
||||||
|
@XmlJavaTypeAdapter(LocalDateAdapter.class)
|
||||||
|
private LocalDate creationDate;
|
||||||
|
/**
|
||||||
|
* Рост человека, может быть null, значение поля должно быть больше 0
|
||||||
|
*/
|
||||||
|
private Integer height;
|
||||||
|
/**
|
||||||
|
* Номер паспорта человека, длина строки должна быть не меньше 8, поле может быть null
|
||||||
|
*/
|
||||||
|
private String passportID;
|
||||||
|
/**
|
||||||
|
* Цвет глаз человека, не может быть null
|
||||||
|
*/
|
||||||
|
private Color eyeColor;
|
||||||
|
/**
|
||||||
|
* Национальность человека, не может быть null
|
||||||
|
*/
|
||||||
|
private Country nationality;
|
||||||
|
/**
|
||||||
|
* Местоположение человека, может быть null
|
||||||
|
*/
|
||||||
|
private Location location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструктор для корректного преобразования человека из xml в объект
|
||||||
|
*/
|
||||||
|
private Person() {
|
||||||
|
this.id = ++existingPeople;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конструктор с параметрами
|
||||||
|
*
|
||||||
|
* @param name Имя человека
|
||||||
|
* @param coordinates Координаты человека
|
||||||
|
* @param height Высота человека
|
||||||
|
* @param passportID Номер паспорта человека
|
||||||
|
* @param eyeColor Цвет глаз человека
|
||||||
|
* @param nationality Национальность человека
|
||||||
|
* @param location Местоположение человека
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Если:
|
||||||
|
* name является пустой строкой,
|
||||||
|
* height меньше 0,
|
||||||
|
* Длина passportID меньше 8 символов
|
||||||
|
*
|
||||||
|
* @throws NullPointerException Если coordinates, eyeColor или nationality являются null
|
||||||
|
*/
|
||||||
|
public Person(String name, @NonNull Coordinates coordinates, Integer height, String passportID,
|
||||||
|
@NonNull Color eyeColor, @NonNull Country nationality, Location location) {
|
||||||
|
this.id = ++existingPeople;
|
||||||
|
this.creationDate = LocalDate.now();
|
||||||
|
this.location = location;
|
||||||
|
this.coordinates = coordinates;
|
||||||
|
this.eyeColor = eyeColor;
|
||||||
|
this.nationality = nationality;
|
||||||
|
this.setName(name);
|
||||||
|
this.setHeight(height);
|
||||||
|
this.setPassportID(passportID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Метод, меняющий все значения полей человека (кроме {@link #id} и {@link #creationDate}) в соответствии
|
||||||
|
* со значениями полей другого
|
||||||
|
*
|
||||||
|
* @param newPerson человек, чьи поля будут присвоены текущему
|
||||||
|
*/
|
||||||
|
public void update(Person newPerson) {
|
||||||
|
this.location = newPerson.location;
|
||||||
|
this.coordinates = newPerson.coordinates;
|
||||||
|
this.eyeColor = newPerson.eyeColor;
|
||||||
|
this.nationality = newPerson.nationality;
|
||||||
|
this.setName(newPerson.name);
|
||||||
|
this.setHeight(newPerson.height);
|
||||||
|
this.setPassportID(newPerson.passportID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сеттер для поля name
|
||||||
|
*
|
||||||
|
* @param name
|
||||||
|
* Имя человека
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* Если имя является пустой строкой
|
||||||
|
*/
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
if (name.isEmpty())
|
||||||
|
throw new IllegalArgumentException("Поле name класса Person не может быть null или пустым");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сеттер для поля height
|
||||||
|
*
|
||||||
|
* @param height Рост человека
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Если рост меньше 0
|
||||||
|
*/
|
||||||
|
public void setHeight(Integer height) {
|
||||||
|
this.height = height;
|
||||||
|
if (height <= 0)
|
||||||
|
throw new IllegalArgumentException("Поле height класса Person должно быть больше 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сеттер для поля passportID
|
||||||
|
*
|
||||||
|
* @param passportID Номер паспорта человека
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException Если номер паспорта меньше 8 символов в длину
|
||||||
|
*/
|
||||||
|
public void setPassportID(String passportID) {
|
||||||
|
this.passportID = passportID;
|
||||||
|
if (passportID.length() < 8)
|
||||||
|
throw new IllegalArgumentException("Поле passportID класса Person не может быть меньше 8 символов в длину");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Переопределенный метод сравнения двух людей,
|
||||||
|
* сравнение производится по имени, номеру паспорта,
|
||||||
|
* росту, национальности, местоположению и цвету глаз
|
||||||
|
*
|
||||||
|
* @param other Объект для сравнения
|
||||||
|
* @return Целое число - результат сравнения
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int compareTo(Person other) {
|
||||||
|
return Comparator.comparing((Person p) -> p.name)
|
||||||
|
.thenComparing(p -> p.passportID)
|
||||||
|
.thenComparing(p -> p.height)
|
||||||
|
.thenComparing(p -> p.nationality)
|
||||||
|
.thenComparing(p -> p.location)
|
||||||
|
.thenComparing(p -> p.eyeColor)
|
||||||
|
.compare(this, other);
|
||||||
|
}
|
||||||
|
}
|
20
src/main/java/ru/erius/lab5/parser/LocalDateAdapter.java
Normal file
20
src/main/java/ru/erius/lab5/parser/LocalDateAdapter.java
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package ru.erius.lab5.parser;
|
||||||
|
|
||||||
|
import javax.xml.bind.annotation.adapters.XmlAdapter;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс парсера для корректного преобразования объекта LocalDate в xml формат и наоборот
|
||||||
|
*/
|
||||||
|
public class LocalDateAdapter extends XmlAdapter<String, LocalDate> {
|
||||||
|
@Override
|
||||||
|
public LocalDate unmarshal(String v) throws Exception {
|
||||||
|
return LocalDate.parse(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String marshal(LocalDate v) throws Exception {
|
||||||
|
return v.format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||||
|
}
|
||||||
|
}
|
57
src/main/java/ru/erius/lab5/util/UtilFunctions.java
Normal file
57
src/main/java/ru/erius/lab5/util/UtilFunctions.java
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
package ru.erius.lab5.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Класс функций-утилит для избавления от повторяющихся участков кода и выноса их в методы для общего пользования
|
||||||
|
*/
|
||||||
|
public final class UtilFunctions {
|
||||||
|
|
||||||
|
private UtilFunctions() {}
|
||||||
|
|
||||||
|
public static Integer intOrNull(String number) {
|
||||||
|
int result;
|
||||||
|
try {
|
||||||
|
result = Integer.parseInt(number);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long longOrNull(String number) {
|
||||||
|
long result;
|
||||||
|
try {
|
||||||
|
result = Long.parseLong(number);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Float floatOrNull(String number) {
|
||||||
|
float result;
|
||||||
|
try {
|
||||||
|
result = Float.parseFloat(number);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Double doubleOrNull(String number) {
|
||||||
|
double result;
|
||||||
|
try {
|
||||||
|
result = Double.parseDouble(number);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> T enumOrNull(String value, Class<T> enumType) {
|
||||||
|
try {
|
||||||
|
return T.valueOf(enumType, value);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue