diff --git a/src/Main.java b/src/Main.java index 18818d4..9203d7d 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,36 +1,47 @@ -import data.Epic; -import data.Status; -import data.Subtask; -import data.Task; +import data.*; +import exception.ManagerSaveException; +import manager.FileBackedTaskManager; import manager.Managers; import manager.TaskManager; import manager.TaskManagerType; +import java.io.File; import java.util.List; +import java.util.Scanner; public class Main { - static TaskManager manager = Managers.getTaskManager(TaskManagerType.IN_MEMORY); + static TaskManager manager = Managers.getTaskManager(TaskManagerType.FILE_BACKED); + static Scanner scanner = new Scanner(System.in); + static final int ms = 1500; public static void main(String[] args) { - insertStartData(); - - printListAll(); - - testSubtasks(); - - printHistory(); - - testEpics(); - - printHistory(); - - testTasks(); - - printHistory(); - - printListAll(); +// insertStartData(); +// +// waitSecond(ms); +// +// printListAll(); +// +// testSubtasks(); +// +// waitSecond(ms); +// +// printListAll(); +// +// printHistory(); +// +// testEpics(); +// +// waitSecond(ms); +// +// printHistory(); +// +// testTasks(); +// +// printHistory(); + + start(); } /// Ввести исходные данные @@ -40,23 +51,29 @@ static void insertStartData() { "Познакомиться с девушкой", "Она должна понравиться моей маме" )); + waitSecond(ms); Task task2 = manager.setTask(new Task( null, "Сводить девушку на свидание", "По правилам этикета нужно заплатить за нее в ресторане" )); + waitSecond(ms); + Task task3 = manager.setTask(new Task( - null, - "Подарить девушке цветы", - "Не дарить хризантемы" + null, + "Подарить девушке цветы", + "Не дарить хризантемы" )); + waitSecond(ms); Epic epic1 = manager.setEpic(new Epic( null, "Найти девушку для знакомства", "Использовать различные способы поиска, для ускорения процесса" )); + waitSecond(ms); + Subtask subtask1 = manager.setSubtask(new Subtask( null, "Поиск на сайтах знакомств", @@ -64,13 +81,17 @@ static void insertStartData() { null, epic1.getId() )); + waitSecond(ms); + Subtask subtask2 = manager.setSubtask(new Subtask( null, "Поиск через знакомых", null, null, epic1.getId() - )); + )); + waitSecond(ms); + Subtask subtask3 = manager.setSubtask(new Subtask( null, "Поиск в литературном клубе", @@ -78,32 +99,40 @@ static void insertStartData() { null, epic1.getId() )); + waitSecond(ms); Epic epic2 = manager.setEpic(new Epic( null, "Оптимизировать поиск девушки", "Получить результат быстрее и с меньшими затратами" - )); + )); + waitSecond(ms); + Subtask subtask4 = manager.setSubtask(new Subtask( null, "Выбрать наиболее оптимальный способ поиска", null, null, epic2.getId() - )); + )); + waitSecond(ms); Epic epic3 = manager.setEpic(new Epic( null, "Создать систему оценки способов поиска", "Без статистики не обойтись" - )); + )); + waitSecond(ms); + Subtask subtask5 = manager.setSubtask(new Subtask( null, "Задать параметры системы оценки", "Возможно бальная система", null, epic3.getId() - )); + )); + waitSecond(ms); + Subtask subtask6 = manager.setSubtask(new Subtask( null, "Собрать статистику способов", @@ -111,6 +140,8 @@ static void insertStartData() { null, epic3.getId() )); + waitSecond(ms); + Subtask subtask7 = manager.setSubtask(new Subtask( null, "Провести статистическое исследование", @@ -153,10 +184,12 @@ static void testTasks() { Status.IN_PROGRESS ))); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Удаление задачи по идентификатору:" + "\n"); System.out.println(removeTask(2)); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Получение списка всех задач:" + "\n"); System.out.println(getAllTasksList()); @@ -182,6 +215,7 @@ static void testSubtasks() { null ))); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Изменение статуса подзадачи:" + "\n"); System.out.println(manager.updateSubtask(new Subtask( @@ -192,10 +226,12 @@ static void testSubtasks() { null ))); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Удаление подзадачи по идентификатору:" + "\n"); System.out.println(removeSubtask(6)); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Получение списка всех подзадач:" + "\n"); System.out.println(getAllSubtasksList()); @@ -219,10 +255,12 @@ static void testEpics() { "" ))); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Удаление эпика по идентификатору:" + "\n"); System.out.println(removeEpic(8)); System.out.println("\n" + "-".repeat(20) + "\n"); + waitSecond(ms); System.out.println("Получение эпика со списком подзадач:" + "\n"); System.out.println(getEpicWithSubtasksList(10)); @@ -251,7 +289,7 @@ static StringBuilder getListAll() { list.append("Задачи:").append("\n").append(getAllTasksList()).append("\n"); List epics = manager.getAllEpics(); int epicIndex = 1; - for (Epic epic: epics) { + for (Epic epic : epics) { list.append("\n").append("Эпик ").append(epicIndex).append(":\n"); list.append(epic).append("\n"); List subtasks = manager.getAllSubTasksByEpic(epic.getId()); @@ -392,7 +430,7 @@ static StringBuilder getAllSubtasksList() { static StringBuilder getAllSubtasksList(int epicId) { Epic epic = manager.getEpic(epicId); if (epic == null) { - return null; + return null; } StringBuilder subtaskList = new StringBuilder(); List thisSubtasks = manager.getAllSubTasksByEpic(epic.getId()); @@ -409,4 +447,234 @@ static StringBuilder getAllSubtasksList(int epicId) { return subtaskList; } + static void printHelp() { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("Обновить данные --> update"); + System.out.println("Поиск по id --> find"); + System.out.println("Вывести весь список --> print-all"); + System.out.println("Добавить задачу --> add-task"); + System.out.println("Добавить эпик --> add-epic"); + System.out.println("Добавить подзадачу --> add-sub"); + System.out.println("Удалить --> remove"); + System.out.println("Изменить статус (задача или подзадача) --> set-st"); + System.out.println("Показать историю --> history"); + System.out.println("Вывести инструкцию --> help"); + System.out.println("Выход --> exit"); + System.out.println("\n" + "-".repeat(20) + "\n"); + } + + static void start() { + System.out.println("\n" + "-".repeat(20) + "\n"); + + while (true) { + System.out.println("Введите команду или help"); + System.out.print("--> "); + + switch (scanner.next()) { + case "exit": + System.out.println("\n" + "-".repeat(20)); + return; + case "help": + printHelp(); + break; + case "update": + FileBackedTaskManager manager1 = FileBackedTaskManager + .getInstance(new File("src/autosave/data.csv")); + try { + manager1.updateDataFromFile(); + } catch (ManagerSaveException e) { + e.printStackTrace(); + } + System.out.println("Данные загружены."); + break; + case "print-all": + printListAll(); + break; + case "history": + System.out.println("\n" + "-".repeat(20) + "\n"); + printHistory(); + break; + case "find": + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("id --> "); + int foundedId = Integer.parseInt(scanner.next()); + Task findedTask = manager.get(foundedId); + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println(findedTask); + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + case "add-task": + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("Заголовок (без пробелов) --> "); + String taskTitle = scanner.next(); + System.out.print("Описание (без пробелов) --> "); + String taskDescription = scanner.next(); + Task newTsk = new Task(null, taskTitle, taskDescription, null); + manager.setTask(newTsk); + System.out.println("Задача добавлена"); + System.out.println(newTsk); + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + case "add-epic": + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("Заголовок (без пробелов) --> "); + String epicTitle = scanner.next(); + System.out.print("Описание (без пробелов) --> "); + String epicDescription = scanner.next(); + Epic newEp = new Epic(null, epicTitle, epicDescription); + manager.setEpic(newEp); + System.out.println("Эпик добавлен"); + System.out.println(newEp); + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + case "add-sub": + addSub(); + break; + case "set-st": + setST(); + break; + case "remove": + Type rmType = null; + while (true) { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("Назад --> 0"); + System.out.print("id --> "); + int rmId = Integer.parseInt(scanner.next()); + if (rmId == 0) { + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + if (manager.getWithoutHistory(rmId) == null) { + System.out.println("Такого id не существует"); + continue; + } + try { + rmType = manager.getType(rmId); + } catch (NullPointerException e) { + System.out.println("Такого id не существует"); + continue; + } + switch (rmType) { + case TASK -> System.out.println(removeTask(rmId)); + case EPIC -> System.out.println(removeEpic(rmId)); + case SUBTASK -> System.out.println(removeSubtask(rmId)); + } + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + break; + default: + System.out.println("Такой команды нет"); + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + } + } + + static void setStatus(Task taskObject, Status status) { + Type type = manager.getType(taskObject.getId()); + taskObject.setStatus(status); + switch (type) { + case TASK: + manager.updateTask(taskObject); + break; + case EPIC: + manager.updateEpic((Epic) taskObject); + break; + case SUBTASK: + manager.updateSubtask((Subtask) taskObject); + break; + default: + break; + } + } + + static void setST() { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("id --> "); + int statusesId = Integer.parseInt(scanner.next()); + Task statusesTask = manager.getWithoutHistory(statusesId); + if (statusesTask == null) { + System.out.println("id " + statusesId + " не существует"); + System.out.println("\n" + "-".repeat(20) + "\n"); + return; + } + if (manager.getType(statusesId) == Type.EPIC) { + System.out.println("Это id эпика - статус автоматический"); + System.out.println("\n" + "-".repeat(20) + "\n"); + return; + } + while (true) { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("NEW --> 1"); + System.out.println("IN_PROGRESS --> 2"); + System.out.println("DONE --> 3"); + System.out.print("Новый статус № --> "); + String statusNumber = scanner.next(); + switch (statusNumber) { + case "1": + setStatus(statusesTask, Status.NEW); + System.out.println("Статус --> " + Status.NEW); + System.out.println(statusesTask); + break; + case "2": + setStatus(statusesTask, Status.IN_PROGRESS); + System.out.println("Статус --> " + Status.IN_PROGRESS); + System.out.println(statusesTask); + break; + case "3": + setStatus(statusesTask, Status.DONE); + System.out.println("Статус --> " + Status.DONE); + System.out.println(statusesTask); + break; + default: + System.out.println(statusNumber + " - Неверный номер"); + continue; + } + System.out.println("\n" + "-".repeat(20) + "\n"); + break; + } + } + + static void addSub() { + int epiccId; + while (true) { + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.println("Эпики:"); + System.out.println(getAllEpicsList()); + System.out.println("Назад --> 0"); + System.out.print("id эпика --> "); + epiccId = Integer.parseInt(scanner.next()); + if (epiccId == 0) { + System.out.println("\n" + "-".repeat(20) + "\n"); + return; + } + + if (manager.getEpic(epiccId) == null) { + System.out.println("Такого id эпика нет"); + continue; + } + + System.out.println("\n" + "-".repeat(20) + "\n"); + System.out.print("Заголовок (без пробелов) --> "); + String subtaskTitle = scanner.next(); + System.out.print("Описание (без пробелов) --> "); + String subtaskDescription = scanner.next(); + Subtask newSub = new Subtask(null, subtaskTitle, subtaskDescription, null, epiccId); + manager.setSubtask(newSub); + System.out.println("Подзадача добавлена"); + System.out.println(newSub); + System.out.println("\n" + "-".repeat(20) + "\n"); + return; + } + } + + static void waitSecond(int ms) { + try { + Thread.sleep(ms); // Пауза на 1000 миллисекунд (1 секунду) + } catch (InterruptedException e) { + // Обработка исключения, которое может возникнуть, если поток будет прерван + e.printStackTrace(); + } + } } \ No newline at end of file diff --git a/src/data/Epic.java b/src/data/Epic.java index fa3d923..daee0f0 100644 --- a/src/data/Epic.java +++ b/src/data/Epic.java @@ -33,6 +33,19 @@ public void removeSubtaskId(Integer subtaskId) { subtaskIds.remove(subtaskId); } + @Override + public Type getType() { + return Type.EPIC; + } + + @Override + /// Получение копии объекта + public Epic getCopy() { + Epic newEpic = new Epic(this.id, this.title, this.description); + newEpic.setStatus(this.status); + return newEpic; + } + @Override public String toString() { return "Epic{" + diff --git a/src/data/Subtask.java b/src/data/Subtask.java index 25cbbca..1de266e 100644 --- a/src/data/Subtask.java +++ b/src/data/Subtask.java @@ -16,6 +16,17 @@ public Integer getEpicId() { return epicId; } + @Override + public Type getType() { + return Type.SUBTASK; + } + + @Override + /// Получение копии объекта + public Subtask getCopy() { + return new Subtask(this.id, this.title, this.description, this.status, this.epicId); + } + @Override public String toString() { return "Subtask{" + diff --git a/src/data/Task.java b/src/data/Task.java index a2c2150..e960db1 100644 --- a/src/data/Task.java +++ b/src/data/Task.java @@ -2,7 +2,7 @@ import java.util.Objects; -public class Task { +public class Task implements Comparable { /// ID задачи protected Integer id; @@ -71,6 +71,21 @@ public void setStatus(Status status) { this.status = status; } + /// Получение типа объекта + public Type getType() { + return Type.TASK; + } + + /// Получение копии объекта + public Task getCopy() { + return new Task(this.id, this.title, this.description, this.status); + } + + @Override + public int compareTo(Task task) { + return Integer.compare(this.id, task.id); + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; diff --git a/src/data/Type.java b/src/data/Type.java new file mode 100644 index 0000000..6f2e8c6 --- /dev/null +++ b/src/data/Type.java @@ -0,0 +1,5 @@ +package data; + +public enum Type { + TASK, EPIC, SUBTASK +} diff --git a/src/exception/ManagerSaveException.java b/src/exception/ManagerSaveException.java new file mode 100644 index 0000000..ae8c429 --- /dev/null +++ b/src/exception/ManagerSaveException.java @@ -0,0 +1,7 @@ +package exception; + +public class ManagerSaveException extends RuntimeException { + public ManagerSaveException(String message) { + super(message); + } +} diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java new file mode 100644 index 0000000..e6e8ae3 --- /dev/null +++ b/src/manager/FileBackedTaskManager.java @@ -0,0 +1,337 @@ +package manager; + +import data.*; +import exception.ManagerSaveException; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public class FileBackedTaskManager extends InMemoryTaskManager { + + private static File file; + private static FileBackedTaskManager instance; + + /// Параметры CSV файла + private static final String TABLE_HEADER = "id,type,name,status,description,epic"; + private static final Charset CHARSET = StandardCharsets.UTF_8; + private static final String DELIMITER = ","; + private static final String QUOTE = "\""; + + /// Максимальный id + private int maxId = 0; + + /// Буфер чтения + private List fileToList; + + /// Конструктор + FileBackedTaskManager(File newFile) { + try { + loadFile(newFile); + updateDataFromFile(); + } catch (ManagerSaveException e) { + System.out.println(e.getMessage()); + } + } + + /// Получить синглтон класса + public static FileBackedTaskManager getInstance(File newFile) { + if (instance == null || !newFile.getPath().equals(file.getPath())) { + instance = new FileBackedTaskManager(newFile); + } + return instance; + } + + /// Подгрузить файл + private void loadFile(File newFile) throws ManagerSaveException { + if (!newFile.exists()) { + try { + Files.createFile(Path.of(newFile.getPath())); + } catch (IOException e) { + throw new ManagerSaveException("Ошибка создания файла"); + } + System.out.println("Создан новый файл: " + newFile.getPath()); + } + file = newFile; + + readFileToList(); + if (fileToList.isEmpty()) { + resetFile(); + readFileToList(); + } + + String header = fileToList.getFirst(); + if (header == null || !header.equals(TABLE_HEADER)) { + resetFile(); + readFileToList(); + } + } + + /// Перезагрузить данные из файла + public void updateDataFromFile() throws ManagerSaveException { + super.removeAllTasks(); + super.removeAllEpics(); + + if (fileToList.size() > 1) { + setMaxId(); + } + + int rowNumber = 1; + while (rowNumber < fileToList.size()) { + Task taskObject = getDataObjectFromString(fileToList.get(rowNumber)); + + if (taskObject == null) { + return; + } + + maxId = taskObject.getId(); + + if (taskObject instanceof Epic epic) { + idToEpic.put(maxId, epic); + } else if (taskObject instanceof Subtask subtask) { + int epicId = subtask.getEpicId(); + if (epicId == 0) { + System.out.println("Подзадача c id = " + subtask.getId() + " не привязана к эпику. Epic id = 0."); + rowNumber++; + continue; + } + Epic thisEpic = idToEpic.get(epicId); + if (thisEpic == null) { + System.out.println("У подзадачи c id = " + subtask.getId() + + " отсутствует эпик c id = " + epicId + "."); + rowNumber++; + continue; + } + thisEpic.addSubtaskId(maxId); + + idToSubtask.put(maxId, subtask); + updateEpicsStatus(thisEpic.getId()); + } else { + idToTask.put(maxId, taskObject); + } + + rowNumber++; + } + + if (maxId != 0) { + if (counter > maxId) { + maxId = counter; + } else { + counter = maxId + 1; + } + } + + } + + /// Очистить содержимое файла, добавить шапку таблицы + private void resetFile() throws ManagerSaveException { + fileToList.clear(); + fileToList.add(TABLE_HEADER); + + try { + Files.write(file.toPath(), fileToList); + } catch (IOException e) { + throw new ManagerSaveException("Ошибка сброса файла"); + } + } + + /// Выгрузить все данные в файл + private void save() throws ManagerSaveException { + List all = getAll(); + if (all.isEmpty()) { + resetFile(); + readFileToList(); + return; + } + + try { + fileToList.clear(); + fileToList.add(TABLE_HEADER); + for (Task taskObject : all) { + fileToList.add(getStringToFileSave(taskObject)); + } + + Files.write(file.toPath(), fileToList); + + } catch (IOException e) { + throw new ManagerSaveException("Ошибка записи в файл"); + } + } + + private void setMaxId() { + Task lsatTaskObject = getDataObjectFromString(fileToList.getLast()); + if (lsatTaskObject != null) { + maxId = lsatTaskObject.getId(); + } + } + + /// Прочитать содержимое файла в буфер + private void readFileToList() throws ManagerSaveException { + try { + fileToList = Files.readAllLines(file.toPath(), CHARSET); + } catch (IOException e) { + throw new ManagerSaveException("Ошибка чтения из файла"); + } + } + + /// Получить строку для записи в файл из объекта + private String getStringToFileSave(Task taskObject) { + + Integer id = taskObject.getId(); + Type type = getType(id); + + String title = taskObject.getTitle() != null && taskObject.getTitle().contains(DELIMITER) + ? QUOTE + taskObject.getTitle() + QUOTE + : taskObject.getTitle(); + + String description = taskObject.getDescription() != null && taskObject.getDescription().contains(DELIMITER) + ? QUOTE + taskObject.getDescription() + QUOTE + : taskObject.getDescription(); + + String status = taskObject.getStatus() != null ? taskObject.getStatus().toString() : "null"; + + String epic = type.equals(Type.SUBTASK) ? String.valueOf(((Subtask) taskObject).getEpicId()) : ""; + + return id + DELIMITER + + type + DELIMITER + + title + DELIMITER + + status + DELIMITER + + description + DELIMITER + + epic; + } + + /// Получить объект данных из строки + private Task getDataObjectFromString(String stringFromFile) { + try { + if (stringFromFile.isEmpty()) { + throw new ManagerSaveException("Невозможно создать задачу из пустой строки"); + } + + String[] splitStr = stringFromFile.split(DELIMITER + "(?!\\s)"); + if (splitStr.length < 5) { + throw new ManagerSaveException("Не поддерживаемый формат строки"); + } + + Integer id = Integer.parseInt(splitStr[0]); + Type type = Type.valueOf(splitStr[1]); + String title = splitStr[2].replace(QUOTE, ""); + String description = splitStr[4].replace(QUOTE, ""); + Status status = !splitStr[3].equals("null") ? Status.valueOf(splitStr[3]) : null; + + switch (type) { + case TASK: + return new Task(id, title, description, status); + case EPIC: + Epic newEpic = new Epic(id, title, description); + newEpic.setStatus(status); + return newEpic; + case SUBTASK: + int epicId = 0; + try { + epicId = Integer.parseInt(splitStr[5]); + } catch (IndexOutOfBoundsException e) { + // epicId = 0 + } + return new Subtask(id, title, description, status, epicId); + default: + return null; + } + } catch (ManagerSaveException exception) { + System.out.println(exception.getMessage()); + return null; + } + } + + @Override + public Task setTask(Task newTask) { + Task result = super.setTask(newTask); + save(); + setMaxId(); + return result; + } + + @Override + public Epic setEpic(Epic newEpic) { + Epic result = super.setEpic(newEpic); + save(); + setMaxId(); + return result; + } + + @Override + public Subtask setSubtask(Subtask newSubtask) { + Subtask result = super.setSubtask(newSubtask); + save(); + setMaxId(); + return result; + } + + @Override + public Task updateTask(Task newTask) { + Task result = super.updateTask(newTask); + save(); + return result; + } + + @Override + public Epic updateEpic(Epic newEpic) { + Epic result = super.updateEpic(newEpic); + save(); + return result; + } + + @Override + public Subtask updateSubtask(Subtask newSubtask) { + Subtask result = super.updateSubtask(newSubtask); + save(); + return result; + } + + @Override + public boolean removeTask(int taskId) { + boolean result = super.removeTask(taskId); + save(); + return result; + } + + @Override + public boolean removeEpic(int epicId) { + boolean result = super.removeEpic(epicId); + save(); + return result; + } + + @Override + public boolean removeSubtask(int subtaskId) { + boolean result = super.removeSubtask(subtaskId); + save(); + return result; + } + + @Override + public void removeAllSubtasksByEpic(int epicId) { + super.removeAllSubtasksByEpic(epicId); + save(); + } + + @Override + public void removeAllTasks() { + super.removeAllTasks(); + save(); + } + + @Override + public void removeAllEpics() { + super.removeAllEpics(); + save(); + } + + @Override + public void removeAllSubTasks() { + super.removeAllSubTasks(); + save(); + } +} diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 4d17c72..49be0a3 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -1,42 +1,99 @@ package manager; -import data.Epic; -import data.Status; -import data.Subtask; -import data.Task; +import data.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; class InMemoryTaskManager implements TaskManager { /// Хранилище задач - private final Map idToTask = new HashMap<>(); + protected final Map idToTask = new HashMap<>(); /// Хранилище эпиков - private final Map idToEpic = new HashMap<>(); + protected final Map idToEpic = new HashMap<>(); /// Хранилище подзадач - private final Map idToSubtask = new HashMap<>(); + protected final Map idToSubtask = new HashMap<>(); /// Менеджер истории просмотров - HistoryManager historyManager = Managers.getHistoryManager(); + protected final HistoryManager historyManager = Managers.getHistoryManager(); /// Счетчик ID - int counter = 1; + protected int counter = 1; /// Генерация ID private int generateNewId() { return counter++; } + @Override + public Task get(int id) { + if (idToTask.containsKey(id)) { + Task newTask = copyOfTask(id); + historyManager.add(newTask); + return newTask; + } else if (idToEpic.containsKey(id)) { + Epic newEpic = copyOfEpic(id); + historyManager.add(newEpic); + return newEpic; + } else if (idToSubtask.containsKey(id)) { + Subtask newSubtask = copyOfSubtask(id); + historyManager.add(newSubtask); + return newSubtask; + } + System.out.println("Попытка получения несуществующей таски"); + return null; + } + + @Override + public Task getWithoutHistory(int id) { + if (idToTask.containsKey(id)) { + return copyOfTask(id); + } else if (idToEpic.containsKey(id)) { + return copyOfEpic(id); + } else if (idToSubtask.containsKey(id)) { + return copyOfSubtask(id); + } + System.out.println("Попытка получения несуществующей таски"); + return null; + } + + @Override + public Type getType(int id) { + Task taskObject = getWithoutHistory(id); + if (taskObject == null) { + return null; + } + return taskObject.getType(); + } + + public List getAll() { + if (counter == 1) { + return new ArrayList<>(); + } + TreeSet allTasks = new TreeSet<>(); + if (!idToTask.isEmpty()) { + allTasks.addAll(getAllTasks()); + } + if (!idToEpic.isEmpty()) { + allTasks.addAll(getAllEpics()); + } + if (!idToSubtask.isEmpty()) { + allTasks.addAll(getAllSubtasks()); + } + + return new ArrayList<>(allTasks); + } + /// Внесение задачи в хранилище по объекту @Override public Task setTask(Task newTask) { + if (newTask.getType() != Type.TASK) { + System.out.println("Ошибка добавления новой задачи - неверные входные данные."); + return null; + } newTask.setId(generateNewId()); - Task savedTask = new Task(newTask.getId(), newTask.getTitle(), newTask.getDescription(), newTask.getStatus()); + Task savedTask = newTask.getCopy(); idToTask.put(savedTask.getId(), savedTask); return newTask; @@ -45,6 +102,10 @@ public Task setTask(Task newTask) { /// Внесение эпика в хранилище по объекту @Override public Epic setEpic(Epic newEpic) { + if (newEpic.getType() != Type.EPIC) { + System.out.println("Ошибка добавления нового эпика - неверные входные данные."); + return null; + } newEpic.setId(generateNewId()); newEpic.setStatus(Status.NEW); Epic savedEpic = new Epic(newEpic.getId(), newEpic.getTitle(), newEpic.getDescription()); @@ -58,26 +119,21 @@ public Epic setEpic(Epic newEpic) { @Override public Subtask setSubtask(Subtask newSubtask) { Integer epicId = newSubtask.getEpicId(); - if (epicId == null || idToEpic.get(epicId) == null) { + if (epicId == null || idToEpic.get(epicId) == null || newSubtask.getType() != Type.SUBTASK) { + System.out.println("Ошибка добавления новой подзадачи - неверные входные данные."); return null; } Integer subtaskId = generateNewId(); newSubtask.setId(subtaskId); - Subtask savedTask = new Subtask( - newSubtask.getId(), - newSubtask.getTitle(), - newSubtask.getDescription(), - newSubtask.getStatus(), - newSubtask.getEpicId() - ); + Subtask savedTask = newSubtask.getCopy(); Epic epic = idToEpic.get(epicId); epic.addSubtaskId(subtaskId); idToSubtask.put(subtaskId, savedTask); - updateEpicsStatus(epicId); + updateEpicsStatus(epic.getId()); return newSubtask; } @@ -85,16 +141,10 @@ public Subtask setSubtask(Subtask newSubtask) { /// Получение задачи по ID @Override public Task getTask(int taskID) { - Task task = idToTask.getOrDefault(taskID, null); - if (task == null) { + Task newTask = copyOfTask(taskID); + if (newTask == null) { return null; } - Task newTask = new Task( - task.getId(), - task.getTitle(), - task.getDescription(), - task.getStatus() - ); historyManager.add(newTask); return newTask; } @@ -102,19 +152,10 @@ public Task getTask(int taskID) { /// Получение эпика по ID @Override public Epic getEpic(int epicID) { - Epic epic = idToEpic.getOrDefault(epicID, null); - if (epic == null) { + Epic newEpic = copyOfEpic(epicID); + if (newEpic == null) { return null; } - Epic newEpic = new Epic( - epic.getId(), - epic.getTitle(), - epic.getDescription() - ); - newEpic.setStatus(epic.getStatus()); - for (int subtaskId : epic.getSubtaskIds()) { - newEpic.addSubtaskId(subtaskId); - } historyManager.add(newEpic); return newEpic; } @@ -122,37 +163,67 @@ public Epic getEpic(int epicID) { /// Получение подзадачи по ID @Override public Subtask getSubtask(int subTaskID) { - Subtask subtask = idToSubtask.getOrDefault(subTaskID, null); - if (subtask == null) { + Subtask newSubtask = copyOfSubtask(subTaskID); + if (newSubtask == null) { return null; } - Subtask newSubtask = new Subtask( - subtask.getId(), - subtask.getTitle(), - subtask.getDescription(), - subtask.getStatus(), - subtask.getEpicId() - ); historyManager.add(newSubtask); return newSubtask; } + /// Получение копии задачи по ID + private Task copyOfTask(int taskId) { + if (!idToTask.containsKey(taskId)) { + return null; + } + Task task = idToTask.get(taskId); + return task.getCopy(); + } + + /// Получение копии эпика по ID + private Epic copyOfEpic(int epicId) { + if (!idToEpic.containsKey(epicId)) { + return null; + } + Epic epic = idToEpic.get(epicId); + Epic newEpic = epic.getCopy(); + for (int subtaskId : epic.getSubtaskIds()) { + newEpic.addSubtaskId(subtaskId); + } + return newEpic; + } + + /// Получение копии подзадачи по ID + private Subtask copyOfSubtask(int subtaskId) { + if (!idToSubtask.containsKey(subtaskId)) { + return null; + } + Subtask subtask = idToSubtask.get(subtaskId); + return subtask.getCopy(); + } + /// Получение списка всех задач в виде объекта @Override public ArrayList getAllTasks() { - return new ArrayList<>(idToTask.values()); + ArrayList allTasks = new ArrayList<>(idToTask.values()); + allTasks.sort(Task::compareTo); + return allTasks; } /// Получение списка всех эпиков в виде объекта @Override public ArrayList getAllEpics() { - return new ArrayList<>(idToEpic.values()); + ArrayList allEpics = new ArrayList<>(idToEpic.values()); + allEpics.sort(Task::compareTo); + return allEpics; } /// Получение списка всех подзадач в виде объекта @Override public ArrayList getAllSubtasks() { - return new ArrayList<>(idToSubtask.values()); + ArrayList allSubtasks = new ArrayList<>(idToSubtask.values()); + allSubtasks.sort(Task::compareTo); + return allSubtasks; } /// Получение списка всех подзадач эпика в виде объекта @@ -359,7 +430,7 @@ public List getHistory() { } /// Обновление статуса эпика - private void updateEpicsStatus(int epicId) { + protected void updateEpicsStatus(int epicId) { Epic epic = idToEpic.get(epicId); if (epic.getStatus() == null) { return; diff --git a/src/manager/Managers.java b/src/manager/Managers.java index 0c3705c..f4ab859 100644 --- a/src/manager/Managers.java +++ b/src/manager/Managers.java @@ -1,5 +1,7 @@ package manager; +import java.io.File; + public class Managers { public static TaskManager getTaskManager(TaskManagerType type) { @@ -8,8 +10,10 @@ public static TaskManager getTaskManager(TaskManagerType type) { return new InMemoryTaskManager(); } case FILE_BACKED -> { - System.out.println("FILE_BACKED will be adding at next sprint"); - return null; + return FileBackedTaskManager.getInstance(new File( + "src/autosave/", + "data.csv" + )); } default -> { return null; diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java index d03e891..0824a25 100644 --- a/src/manager/TaskManager.java +++ b/src/manager/TaskManager.java @@ -3,6 +3,7 @@ import data.Epic; import data.Subtask; import data.Task; +import data.Type; import java.util.List; @@ -49,4 +50,12 @@ public interface TaskManager { void removeAllSubTasks(); List getHistory(); + + List getAll(); + + Task get(int id); + + Task getWithoutHistory(int id); + + Type getType(int id); } diff --git a/test/manager/FileBackedTaskManagerTest.java b/test/manager/FileBackedTaskManagerTest.java new file mode 100644 index 0000000..6f446bc --- /dev/null +++ b/test/manager/FileBackedTaskManagerTest.java @@ -0,0 +1,414 @@ +package manager; + +import data.Epic; +import data.Status; +import data.Subtask; +import data.Task; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class FileBackedTaskManagerTest { + static TaskManager manager; + static TaskManager manager2; + + static Task task; + static int taskId; + static Task savedTask; + static List tasks; + + static Epic epic; + static int epicId; + static Epic savedEpic; + static List epics; + + static List subtasks; + static File file; + + void updateTaskHistory(List history) { + manager.getTask(taskId); + history.clear(); + history.addAll(manager.getHistory()); + } + + @AfterEach + void afterEach() { + file.deleteOnExit(); + } + + @BeforeEach + void beforeEach() throws IOException { + // Создать новый темп-файл + file = File.createTempFile(".test", ".csv", null); + + // Создать новый менеджер задач + manager = new FileBackedTaskManager(file); + + // Создать новую задачу + task = new Task( + 777, + "Test addNewTask title", + "Test addNewTask description" + ); + + // Добавить задачу в базу и получить идентификатор добавленной задачи + taskId = manager.setTask(task).getId(); + + // Получить задачу из базы по идентификатору + savedTask = manager.getTask(taskId); + + // Получить список всех задач + tasks = manager.getAllTasks(); + + // Изменить созданную задачу + manager.updateTask(new Task( + taskId, + null, + null, + Status.IN_PROGRESS + )); + + // Создать новый эпик + epic = new Epic( + 777, + "Test addNewEpic title", + "Test addNewEpic description" + ); + + // Добавить эпик в базу и получить идентификатор добавленного эпика + epicId = manager.setEpic(epic).getId(); + + // Добавить первую подзадачу в созданный эпик + manager.setSubtask(new Subtask( + 777, + "This is first Subtask", + "This is description of first Subtask", + Status.IN_PROGRESS, + epicId + )); + + // Добавить вторую подзадачу в созданный эпик + manager.setSubtask(new Subtask( + null, + "This is third Subtask", + "This is description of third Subtask", + Status.DONE, + epicId + )); + + // Получить все подзадачи + subtasks = manager.getAllSubtasks(); + + // Получить эпик из базы по идентификатору + savedEpic = manager.getEpic(epicId); + + // Получить список всех эпиков + epics = manager.getAllEpics(); + + // Получить подзадачу по ID + manager.getSubtask(4); + + } + + + @Test + void shouldBeNotNullAfterGet() { + assertNotNull(savedTask, "Задача не найдена."); + } + + @Test + void shouldBeEqualsCreatedAndSavedTasks() { + assertEquals(task, savedTask, "Задачи не совпадают."); + } + + @Test + void shouldBeNotNullListAllTasks() { + assertNotNull(tasks, "Задачи не возвращаются."); + } + + @Test + void shouldBeOneElementOfListAllTasks() { + assertEquals(1, tasks.size(), "Неверное количество задач."); + } + + @Test + void shouldBeEqualsCreatedTaskAndTaskOfListAllTasks() { + assertEquals(task, tasks.getFirst(), "Созданная задача не соответствует задаче из списка."); + } + + @Test + void shouldBeOneElementOfHistoryList() { + List history = new ArrayList<>(manager.getHistory()); + assertEquals(3, history.size(), "Неверное количество задач в истории."); + } + + @Test + void shouldBeEqualsCreatedTaskAndTaskOfHistoryList() { + ArrayList history = new ArrayList<>(manager.getHistory()); + assertEquals(task, history.getFirst(), "Созданная задача не соответствует задаче из истории"); + } + + @Test + void shouldBeTwoElementOfHistoryListAfterUpdateAndGet() { + List history = new ArrayList<>(manager.getHistory()); + updateTaskHistory(history); + assertEquals(3, history.size(), "Неверное количество задач в истории."); + } + + @Test + void shouldBeStatusEqualsOfCreatedTaskAndFirstTaskOfHistoryListAfterUpdateAndGet() { + manager.updateTask(new Task(1, null, null, Status.NEW)); + List history = new ArrayList<>(manager.getHistory()); + updateTaskHistory(history); + Task testedTask = manager.getTask(1); + assertEquals(testedTask.getStatus(), history.getLast().getStatus(), + "Статус созданной задачи не соответствует статусу последней задачи из истории." + ); + } + + @Test + void shouldBeStatusEqualsOfSavedTaskAndLastTaskOfHistoryListAfterUpdateAndGet() { + List history = new ArrayList<>(manager.getHistory()); + updateTaskHistory(history); + assertEquals(manager.getTask(1).getStatus(), history.getLast().getStatus(), + "Статус сохраненной задачи не соответствует статусу последней задачи из истории." + ); + } + + @Test + void shouldBeNotNullAfterGetEpic() { + assertNotNull(savedEpic, "Эпик не найден."); + } + + @Test + void shouldBeEqualsCreatedAndReceivedEpics() { + assertEquals(epic, savedEpic, "Эпики не совпадают."); + } + + @Test + void shouldBeNotNullListAllEpics() { + assertNotNull(epics, "Эпики не возвращаются."); + } + + @Test + void shouldBeOneElementOfListAllEpics() { + assertEquals(1, epics.size(), "Получено неверное количество эпиков."); + } + + @Test + void shouldBeEqualsCreatedEpicAndTaskOfListAllEpics() { + assertEquals(epic, epics.getFirst(), "Созданный эпик не соответствует эпику из списка."); + } + + @Test + void shouldUpdateEpicTitleWhenUpdateEpic() { + manager.updateEpic(new Epic(2, "HelloWorld!", null)); + assertEquals("HelloWorld!", manager.getEpic(2).getTitle(), "Менеджер неправильно обновляет эпик."); + } + + @Test + void shouldReturnTwoAfterSetNewIdOfCashedEpic() { + manager.getEpic(2).setId(3); + int testEpicId = manager.getAllEpics().getLast().getId(); + assertEquals(2, testEpicId, + "Изменение эпика через сеттер влияет на эпик хранящийся в менеджере"); + } + + @Test + void shouldBeNotNullListSubtasksOfSavedEpic() { + assertNotNull(savedEpic.getSubtaskIds(), "Сохраненный эпик не возвращает идентификаторы подзадач."); + } + + @Test + void shouldBeTwoSubtaskIdsContainsOfSavedEpicAfterRemoveSubtask() { + manager.removeSubtask(3); + Epic testedEpic = manager.getEpic(2); + assertEquals(1, testedEpic.getSubtaskIds().size(), + "Сохраненный эпик возвращает неверное количество идентификаторов подзадач " + + "после удаления подзадачи."); + } + + @Test + void shouldBeIsEmptySubtaskListAfterRemoveAllSubtasks() { + manager.removeAllSubtasksByEpic(epicId); + Epic testedEpic = manager.getEpic(epicId); + assertEquals("[]", testedEpic.getSubtaskIds().toString(), + "Список идентификаторов подзадач эпика должен быть пуст после удаления всех подзадач эпика."); + } + + @Test + void shouldContainsTwoElementsInSubtasksList() { + assertEquals(2, subtasks.size(), "Список всех подзадач возвращает не верное количество."); + } + + @Test + void shouldReturnStatusNewWhenAllSubtasksInStatusesNull() { + manager.setSubtask(new Subtask(null, null, null, null, 2)); + manager.setSubtask(new Subtask(null, null, null, null, 2)); + manager.removeSubtask(3); + manager.removeSubtask(4); + assertEquals(Status.NEW, manager.getEpic(2).getStatus(), + "Эпик не выставляет статус NEW когда все подзадачи в статусе null"); + } + + @Test + void shouldReturnNullWhenAddingSubtaskWithMissingId() { + try { + manager.setSubtask(new Subtask(null, "Hello", "World", null, 777)); + } catch (NullPointerException e) { + System.out.print(""); + } + assertEquals(2, subtasks.size(), "Подзадача добавляется в несуществующий эпик."); + } + + @Test + void shouldEqualsTwoWhenRemoveAllSubtaskWithMissingEpicId() { + manager.removeAllSubtasksByEpic(777); + assertEquals(2, subtasks.size(), + "Менеджер удаляет подзадачи несуществующего эпика" + ); + } + + @Test + void shouldTwoWhenGetAllSubtasksByEpic() { + assertEquals(2, manager.getAllSubTasksByEpic(2).size(), + "Менеджер возвращает не верное количество подзадач при вызове всех подзадач эпика"); + } + + @Test + void shouldReturnThreeAndFourWhenGetAllSubtasksIdsByEpic() { + Epic testEpic = manager.getEpic(2); + assertEquals(3, testEpic.getSubtaskIds().getFirst()); + assertEquals(4, testEpic.getSubtaskIds().getLast(), + "Внутри эпиков остаются неактуальные id подзадач."); + } + + @Test + void shouldReturnNullWhenTryingToGetAllSubtasks() { + assertNull(manager.getAllSubTasksByEpic(100), + "Менеджер не возвращает null при попытке получения всех подзадач несуществующего эпика"); + } + + @Test + void shouldUpdateEpicStatusWhenAllHimSubtasksIsDone() { + manager.updateSubtask(new Subtask(3, null, null, Status.DONE, null)); + assertEquals(Status.DONE, manager.getAllEpics().getLast().getStatus(), + "Статус эпика не обновляется когда все его подзадачи завершены"); + } + + @Test + void shouldReturnNullWhenGettingRemovedTask() { + manager.removeTask(1); + assertNull(manager.getTask(1), "Менеджер не удаляет задачу."); + } + + @Test + void shouldReturnNullWhenGettingRemovedSubtask() { + manager.removeSubtask(4); + assertNull(manager.getSubtask(4), "Менеджер не удаляет подзадачу."); + } + + @Test + void shouldReturnNullWhenGettingRemovedEpic() { + manager.removeEpic(2); + assertNull(manager.getEpic(2), "Менеджер не удаляет эпик."); + } + + @Test + void shouldReturnEmptyAfterRemoveAllSubtasks() { + manager.removeAllSubTasks(); + assertEquals("[]", manager.getAllSubtasks().toString(), + "Менеджер не очищает все подзадачи."); + } + + @Test + void shouldReturnEpicStatusNewAfterRemoveAllSubtasks() { + manager.removeAllSubTasks(); + Epic testedEpic = manager.getEpic(2); + assertEquals(Status.NEW, testedEpic.getStatus(), + "Менеджер не присваивает эпикам статус NEW после очистки главного хранилища подзадач."); + } + + @Test + void shouldReturnNullAfterRemoveAllEpics() { + manager.removeAllEpics(); + assertEquals("[]", manager.getAllSubtasks().toString(), + "Менеджер не очищает хранилище подзадач после очистки хранилища эпиков."); + } + + @Test + void shouldReturnTwoWhenAllTasksIsDeleted() { + manager.removeAllTasks(); + assertEquals(2, manager.getHistory().getFirst().getId(), + "При удалении всех задач они не удаляются из истории" + ); + } + + @Test + void shouldReturnListWithOneElementWhenAllEpicsIsDeleted() { + manager.removeAllEpics(); + assertEquals(1, manager.getHistory().size(), + "При удалении всех эпиков эпики не удаляются из истории с их подзадачами" + ); + } + + @Test + void shouldReturnTwoWhenAllSubtasksIsDeleted() { + manager.removeAllSubTasks(); + assertEquals(2, manager.getHistory().size(), + "При удалении всех подзадач они не удаляются из истории" + ); + } + + @Test + void shouldReturnTwoWhenAllSubtasksByEpicIsDeleted() { + manager.setEpic(new Epic(null, null, null)); + manager.setSubtask(new Subtask(null, null, null, null, 5)); + manager.getSubtask(6); + manager.removeAllSubtasksByEpic(2); + assertEquals(3, manager.getHistory().size(), + "При удалении всех подзадач эпика они не удаляются из истории" + ); + } + + @Test + void shouldReturnFourWhenGetAllTaskCountFromFile() { + manager2 = new FileBackedTaskManager(file); + assertEquals(4, manager2.getAll().size(), + "Менеджер загрузил не верное количество задач из файла"); + } + + @Test + void shouldReturnActualStatusesAfterUpdateDataFromFile() { + manager.removeTask(1); + manager.setTask(new Task(null, "LastTask", "eee", Status.NEW)); + manager2 = new FileBackedTaskManager(file); + StringBuilder statuses = new StringBuilder(); + for (Task taskObject : manager2.getAll()) { + statuses.append(taskObject.getStatus()).append(","); + } + statuses.deleteCharAt(statuses.length() - 1); + + assertEquals("IN_PROGRESS,IN_PROGRESS,DONE,NEW", statuses.toString(), + "Менеджер неправильно загружает статусы из файла" + ); + } + + @Test + void shouldReturnNullWhenGetFakeTaskAfterThisAdding() { + // Фэйковая задача + Task subtask = new Subtask(null, "last subtask", null, null, 2); + manager.setTask(subtask); + assertNull(manager.get(5), "Менеджер сохранил фэйковую задачу."); + } + +} \ No newline at end of file diff --git a/test/manager/HistoryManagerTest.java b/test/manager/HistoryManagerTest.java index b060ebf..e38dab4 100644 --- a/test/manager/HistoryManagerTest.java +++ b/test/manager/HistoryManagerTest.java @@ -5,16 +5,14 @@ import data.Subtask; import data.Status; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.*; -public class HistoryManagerTest { +public class HistoryManagerTest { static HistoryManager historyManager; static List history; diff --git a/test/manager/TaskManagerTest.java b/test/manager/TaskManagerTest.java index a1ec34d..5d1ee2e 100644 --- a/test/manager/TaskManagerTest.java +++ b/test/manager/TaskManagerTest.java @@ -13,7 +13,7 @@ import static org.junit.jupiter.api.Assertions.*; -public class TaskManagerTest { +public class TaskManagerTest { static TaskManager manager; @@ -48,6 +48,7 @@ void beforeEach() { ); // Добавить задачу в базу и получить идентификатор добавленной задачи + assertNotNull(manager); taskId = manager.setTask(task).getId(); // Получить задачу из базы по идентификатору @@ -84,15 +85,6 @@ void beforeEach() { )); // Добавить вторую подзадачу в созданный эпик - manager.setSubtask(new Subtask( - null, - "This is second Subtask", - "This is description of second Subtask", - null, - null - )); - - // Добавить третью подзадачу в созданный эпик manager.setSubtask(new Subtask( null, "This is third Subtask", @@ -148,7 +140,7 @@ void shouldBeOneElementOfHistoryList() { @Test void shouldBeEqualsCreatedTaskAndTaskOfHistoryList() { - ArrayList history = (ArrayList) manager.getHistory(); + List history = new ArrayList<>(manager.getHistory()); assertEquals(task, history.getFirst(), "Созданная задача не соответствует задаче из истории"); } @@ -257,7 +249,11 @@ void shouldReturnStatusNewWhenAllSubtasksInStatusesNull() { @Test void shouldReturnNullWhenAddingSubtaskWithMissingId() { - manager.setSubtask(new Subtask(null, "Hello", "World", null, 777)); + try { + manager.setSubtask(new Subtask(null, "Hello", "World", null, 777)); + } catch (NullPointerException e) { + System.out.print(""); + } assertEquals(2, subtasks.size(), "Подзадача добавляется в несуществующий эпик."); }