From 21b0505a1e86ec1be4ba819afd3db99522ff6aa2 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 20:47:38 +0300 Subject: [PATCH 01/21] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=20HistoryManager=20=D1=81=20=D0=B4?= =?UTF-8?q?=D0=B2=D1=83=D1=81=D0=B2=D1=8F=D0=B7=D0=BD=D1=8B=D0=BC=20=D1=81?= =?UTF-8?q?=D0=BF=D0=B8=D1=81=D0=BA=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/HistoryManager.java | 1 + src/manager/InMemoryHistoryManager.java | 88 +++++++++++++- src/manager/InMemoryTaskManager.java | 127 ++++++++++++--------- test/manager/InMemoryTaskManagerTest.java | 105 +++++++++++++++++ test/manager/TaskInheritanceTest.java | 37 ------ test/model/InMemoryHistoryManagerTest.java | 92 +++++++++++++++ test/model/TaskIdConflictTest.java | 2 + test/model/TaskImmutabilityTest.java | 40 ------- 8 files changed, 358 insertions(+), 134 deletions(-) create mode 100644 test/manager/InMemoryTaskManagerTest.java delete mode 100644 test/manager/TaskInheritanceTest.java create mode 100644 test/model/InMemoryHistoryManagerTest.java delete mode 100644 test/model/TaskImmutabilityTest.java diff --git a/src/manager/HistoryManager.java b/src/manager/HistoryManager.java index 6070997..079236c 100644 --- a/src/manager/HistoryManager.java +++ b/src/manager/HistoryManager.java @@ -5,5 +5,6 @@ public interface HistoryManager { void add(Task task); + void remove(int id); List getHistory(); } \ No newline at end of file diff --git a/src/manager/InMemoryHistoryManager.java b/src/manager/InMemoryHistoryManager.java index aee6b0d..c9b1a5b 100644 --- a/src/manager/InMemoryHistoryManager.java +++ b/src/manager/InMemoryHistoryManager.java @@ -2,24 +2,100 @@ import model.Task; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class InMemoryHistoryManager implements HistoryManager { - private static final int MAX_HISTORY_SIZE = 10; - private final List history = new ArrayList<>(); + private static class Node { + Task task; + Node prev; + Node next; + + Node(Task task) { + this.task = task; + } + } + + private final Map historyMap = new HashMap<>(); + private Node head; + private Node tail; @Override public void add(Task task) { if (task == null) return; - if (history.size() >= MAX_HISTORY_SIZE) { - history.removeFirst(); // Удаляем самый старый элемент + // Удаляем существующую задачу из истории, если она есть + remove(task.getId()); + + // Добавляем задачу в конец списка + Node newNode = linkLast(task); + historyMap.put(task.getId(), newNode); + } + + @Override + public void remove(int id) { + Node node = historyMap.get(id); + if (node != null) { + removeNode(node); + historyMap.remove(id); } - history.add(task); } @Override public List getHistory() { - return new ArrayList<>(history); + return getTasks(); + } + + private Node linkLast(Task task) { + Node newNode = new Node(task); + + if (tail == null) { + // Список пустой + head = newNode; + tail = newNode; + } else { + // Добавляем в конец списка + tail.next = newNode; + newNode.prev = tail; + tail = newNode; + } + + return newNode; + } + + private void removeNode(Node node) { + if (node == null) return; + + // Обновляем ссылки соседних узлов + if (node.prev != null) { + node.prev.next = node.next; + } else { + // Удаляем голову + head = node.next; + } + + if (node.next != null) { + node.next.prev = node.prev; + } else { + // Удаляем хвост + tail = node.prev; + } + + // Очищаем ссылки удаляемого узла + node.prev = null; + node.next = null; + } + + private List getTasks() { + List tasks = new ArrayList<>(); + Node current = head; + + while (current != null) { + tasks.add(current.task); + current = current.next; + } + + return tasks; } } \ No newline at end of file diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 9b107ab..a84675f 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -19,7 +19,7 @@ public InMemoryTaskManager() { private int generateId() { return nextId++; } - // Модифицируем методы получения задач для добавления в историю + // Методы получения задач с добавлением в историю @Override public Task getTaskById(int id) { Task task = tasks.get(id); @@ -47,40 +47,64 @@ public Subtask getSubtaskById(int id) { return subtask; } - // Добавляем метод для получения истории @Override public List getHistory() { return historyManager.getHistory(); } - // Остальные методы остаются без изменений... - @Override public List getAllTasks() { return new ArrayList<>(tasks.values()); } - + // Модифицируем методы удаления для очистки истории @Override - public void deleteAllTasks() { tasks.clear(); } - - @Override - public Task createTask(Task task) { - if (task == null) return null; - Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); - tasks.put(newTask.getId(), newTask); - return newTask; + public void deleteTaskById(int id) { + tasks.remove(id); + historyManager.remove(id); } @Override - public void updateTask(Task task) { - if (task == null || !tasks.containsKey(task.getId())) return; - tasks.put(task.getId(), task); + public void deleteEpicById(int id) { + Epic epic = epics.get(id); + if (epic != null) { + // Удаляем эпик и все его подзадачи из истории + historyManager.remove(id); + for (Integer subtaskId : epic.getSubtaskIds()) { + subtasks.remove(subtaskId); + historyManager.remove(subtaskId); + } + epics.remove(id); + } } @Override - public void deleteTaskById(int id) { tasks.remove(id); } + public void deleteSubtaskById(int id) { + Subtask subtask = subtasks.get(id); + if (subtask != null) { + Epic epic = epics.get(subtask.getEpicId()); + if (epic != null) { + epic.removeSubtaskId(id); + updateEpicStatus(epic.getId()); + } + subtasks.remove(id); + historyManager.remove(id); + } + } @Override - public List getAllEpics() { return new ArrayList<>(epics.values()); } + public void deleteAllTasks() { + for (Integer taskId : tasks.keySet()) { + historyManager.remove(taskId); + } + tasks.clear(); + } @Override public void deleteAllEpics() { + // Удаляем все эпики и подзадачи из истории + for (Integer epicId : epics.keySet()) { + historyManager.remove(epicId); + } + for (Integer subtaskId : subtasks.keySet()) { + historyManager.remove(subtaskId); + } + for (Epic epic : epics.values()) { for (Integer subtaskId : epic.getSubtaskIds()) { subtasks.remove(subtaskId); @@ -89,6 +113,40 @@ public void deleteAllEpics() { epics.clear(); } + @Override + public void deleteAllSubtasks() { + // Удаляем все подзадачи из истории + for (Integer subtaskId : subtasks.keySet()) { + historyManager.remove(subtaskId); + } + + for (Epic epic : epics.values()) { + epic.clearSubtasks(); + updateEpicStatus(epic.getId()); + } + subtasks.clear(); + } + + // Остальные методы остаются без изменений... + @Override public List getAllTasks() { return new ArrayList<>(tasks.values()); } + + @Override + public Task createTask(Task task) { + if (task == null) return null; + Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); + tasks.put(newTask.getId(), newTask); + return newTask; + } + + @Override + public void updateTask(Task task) { + if (task == null || !tasks.containsKey(task.getId())) return; + tasks.put(task.getId(), task); + } + + @Override + public List getAllEpics() { return new ArrayList<>(epics.values()); } + @Override public Epic createEpic(Epic epic) { if (epic == null) return null; @@ -106,29 +164,9 @@ public void updateEpic(Epic epic) { existingEpic.setDescription(epic.getDescription()); } - @Override - public void deleteEpicById(int id) { - Epic epic = epics.get(id); - if (epic != null) { - for (Integer subtaskId : epic.getSubtaskIds()) { - subtasks.remove(subtaskId); - } - epics.remove(id); - } - } - @Override public List getAllSubtasks() { return new ArrayList<>(subtasks.values()); } - @Override - public void deleteAllSubtasks() { - for (Epic epic : epics.values()) { - epic.clearSubtasks(); - updateEpicStatus(epic.getId()); - } - subtasks.clear(); - } - @Override public Subtask createSubtask(Subtask subtask) { if (subtask == null || !epics.containsKey(subtask.getEpicId())) return null; @@ -163,19 +201,6 @@ public void updateSubtask(Subtask subtask) { updateEpicStatus(subtask.getEpicId()); } - @Override - public void deleteSubtaskById(int id) { - Subtask subtask = subtasks.get(id); - if (subtask != null) { - Epic epic = epics.get(subtask.getEpicId()); - if (epic != null) { - epic.removeSubtaskId(id); - updateEpicStatus(epic.getId()); - } - subtasks.remove(id); - } - } - @Override public List getSubtasksByEpicId(int epicId) { List result = new ArrayList<>(); diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java new file mode 100644 index 0000000..2417438 --- /dev/null +++ b/test/manager/InMemoryTaskManagerTest.java @@ -0,0 +1,105 @@ +package manager; + +import model.Task; +import model.Epic; +import model.Subtask; +import model.Status; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +class InMemoryTaskManagerTest { + private TaskManager taskManager; + + @BeforeEach + void setUp() { + taskManager = new InMemoryTaskManager(); + } + + @Test + void testTaskImmutability() { + // Создаем задачу через менеджер + Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); + int originalId = task.getId(); + String originalName = task.getName(); + + // Пытаемся изменить задачу через сеттеры + task.setId(999); + task.setName("Modified"); + task.setStatus(Status.DONE); + + // Получаем задачу из менеджера + Task managedTask = taskManager.getTaskById(originalId); + + // Проверяем, что данные в менеджере не изменились + assertEquals(originalId, managedTask.getId()); + assertEquals(originalName, managedTask.getName()); + assertEquals(Status.NEW, managedTask.getStatus()); + } + + @Test + void testEpicImmutability() { + Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); + int originalId = epic.getId(); + + // Пытаемся изменить эпик + epic.setId(999); + epic.setName("Modified"); + + // Получаем эпик из менеджера + Epic managedEpic = taskManager.getEpicById(originalId); + + // Проверяем, что данные в менеджере не изменились + assertEquals(originalId, managedEpic.getId()); + assertEquals("Test Epic", managedEpic.getName()); + } + + @Test + void testHistoryWithoutDuplicates() { + Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); + + // Многократно запрашиваем одну и ту же задачу + taskManager.getTaskById(task.getId()); + taskManager.getTaskById(task.getId()); + taskManager.getTaskById(task.getId()); + + // В истории должна быть только одна запись + assertEquals(1, taskManager.getHistory().size()); + } + + @Test + void testDataIntegrityAfterDeletion() { + Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); + Subtask subtask = taskManager.createSubtask(new Subtask("Subtask", "Desc", Status.NEW, epic.getId())); + + int subtaskId = subtask.getId(); + int epicId = epic.getId(); + + // Удаляем подзадачу + taskManager.deleteSubtaskById(subtaskId); + + // Проверяем, что эпик не содержит удаленную подзадачу + Epic updatedEpic = taskManager.getEpicById(epicId); + assertFalse(updatedEpic.getSubtaskIds().contains(subtaskId)); + + // Проверяем, что подзадача удалена из хранилища + assertNull(taskManager.getSubtaskById(subtaskId)); + } + + @Test + void testEmptyHistoryAfterTaskDeletion() { + Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); + + // Добавляем в историю + taskManager.getTaskById(task.getId()); + assertEquals(1, taskManager.getHistory().size()); + + // Удаляем задачу + taskManager.deleteTaskById(task.getId()); + + // История должна быть пустой + assertTrue(taskManager.getHistory().isEmpty()); + } +} \ No newline at end of file diff --git a/test/manager/TaskInheritanceTest.java b/test/manager/TaskInheritanceTest.java deleted file mode 100644 index b723370..0000000 --- a/test/manager/TaskInheritanceTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package manager; - -import model.Epic; -import model.Status; -import model.Subtask; -import model.Task; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class TaskInheritanceTest { - - @Test - void taskAndSubtaskWithSameIdShouldNotBeEqual() { - Task task = new Task("Task", "Description", 1, Status.NEW); - Subtask subtask = new Subtask("Subtask", "Description", 1, Status.NEW, 2); - - assertNotEquals(task, subtask, "Task и Subtask с одинаковым ID не должны быть равны"); - } - - @Test - void subtasksWithSameIdShouldBeEqual() { - Subtask subtask1 = new Subtask("Subtask 1", "Description 1", 1, Status.NEW, 10); - Subtask subtask2 = new Subtask("Subtask 2", "Description 2", 1, Status.DONE, 20); - - assertEquals(subtask1, subtask2, "Subtask с одинаковым ID должны быть равны"); - } - - @Test - void epicsWithSameIdShouldBeEqual() { - Epic epic1 = new Epic("Epic 1", "Description 1"); - epic1.setId(1); - Epic epic2 = new Epic("Epic 2", "Description 2"); - epic2.setId(1); - - assertEquals(epic1, epic2, "Epic с одинаковым ID должны быть равны"); - } -} \ No newline at end of file diff --git a/test/model/InMemoryHistoryManagerTest.java b/test/model/InMemoryHistoryManagerTest.java new file mode 100644 index 0000000..3dbf32a --- /dev/null +++ b/test/model/InMemoryHistoryManagerTest.java @@ -0,0 +1,92 @@ +package manager; + +import model.Task; +import model.Status; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; + +class InMemoryHistoryManagerTest { + private HistoryManager historyManager; + + @BeforeEach + void setUp() { + historyManager = new InMemoryHistoryManager(); + } + + @Test + void testAddAndGetHistory() { + Task task = new Task("Test", "Description", Status.NEW); + task.setId(1); + + historyManager.add(task); + List history = historyManager.getHistory(); + + assertEquals(1, history.size()); + assertEquals(task, history.get(0)); + } + + @Test + void testRemoveDuplicates() { + Task task = new Task("Test", "Description", Status.NEW); + task.setId(1); + + historyManager.add(task); + historyManager.add(task); // Дубликат + historyManager.add(task); // Еще один дубликат + + List history = historyManager.getHistory(); + assertEquals(1, history.size()); + } + + @Test + void testRemoveFromHistory() { + Task task1 = new Task("Test1", "Description1", Status.NEW); + Task task2 = new Task("Test2", "Description2", Status.NEW); + task1.setId(1); + task2.setId(2); + + historyManager.add(task1); + historyManager.add(task2); + + historyManager.remove(1); + List history = historyManager.getHistory(); + + assertEquals(1, history.size()); + assertEquals(task2, history.get(0)); + } + + @Test + void testHistoryOrderPreservation() { + Task task1 = new Task("Test1", "Description1", Status.NEW); + Task task2 = new Task("Test2", "Description2", Status.NEW); + Task task3 = new Task("Test3", "Description3", Status.NEW); + task1.setId(1); + task2.setId(2); + task3.setId(3); + + historyManager.add(task1); + historyManager.add(task2); + historyManager.add(task3); + + List history = historyManager.getHistory(); + + assertEquals(3, history.size()); + assertEquals(task1, history.get(0)); + assertEquals(task2, history.get(1)); + assertEquals(task3, history.get(2)); + } + + @Test + void testRemoveNonExistentTask() { + // Удаление несуществующей задачи не должно вызывать исключений + assertDoesNotThrow(() -> historyManager.remove(999)); + } + + @Test + void testEmptyHistory() { + assertTrue(historyManager.getHistory().isEmpty()); + } +} \ No newline at end of file diff --git a/test/model/TaskIdConflictTest.java b/test/model/TaskIdConflictTest.java index 222e673..571204d 100644 --- a/test/model/TaskIdConflictTest.java +++ b/test/model/TaskIdConflictTest.java @@ -2,6 +2,8 @@ import model.Task; import model.Status; +import manager.InMemoryTaskManager; +import manager.TaskManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; diff --git a/test/model/TaskImmutabilityTest.java b/test/model/TaskImmutabilityTest.java deleted file mode 100644 index ff8b490..0000000 --- a/test/model/TaskImmutabilityTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package model; - -import model.Task; -import model.Status; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class TaskImmutabilityTest { - private TaskManager taskManager; - - @BeforeEach - void setUp() { - taskManager = new InMemoryTaskManager(); - } - - @Test - void taskRemainsUnchangedWhenAddedToManager() { - // Создаем оригинальную задачу - Task originalTask = new Task("Original", "Original Description", Status.NEW); - - // Сохраняем оригинальные значения - String originalName = originalTask.getName(); - String originalDescription = originalTask.getDescription(); - Status originalStatus = originalTask.getStatus(); - - // Добавляем в менеджер - Task createdTask = taskManager.createTask(originalTask); - - // Проверяем, что оригинальная задача не изменилась - assertEquals(originalName, originalTask.getName(), "Оригинальное имя не должно измениться"); - assertEquals(originalDescription, originalTask.getDescription(), "Оригинальное описание не должно измениться"); - assertEquals(originalStatus, originalTask.getStatus(), "Оригинальный статус не должен измениться"); - - // Проверяем, что созданная задача имеет те же значения - assertEquals(originalName, createdTask.getName(), "Имя должно сохраниться"); - assertEquals(originalDescription, createdTask.getDescription(), "Описание должно сохраниться"); - assertEquals(originalStatus, createdTask.getStatus(), "Статус должен сохраниться"); - } -} \ No newline at end of file From 5a7829321a9ad7c4c690da4d4928866b2e4e6c0a Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 20:57:00 +0300 Subject: [PATCH 02/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20Checkstyle=20=D0=B2=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B5?= =?UTF-8?q?=20Subtask?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/model/Epic.java | 25 +++++++++++++++++++----- src/model/Subtask.java | 9 +++++++-- src/model/Task.java | 43 +++++++++++++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/src/model/Epic.java b/src/model/Epic.java index 6634160..c4569f8 100644 --- a/src/model/Epic.java +++ b/src/model/Epic.java @@ -11,17 +11,32 @@ public Epic(String name, String description) { this.subtaskIds = new ArrayList<>(); } - public List getSubtaskIds() { return new ArrayList<>(subtaskIds); } - public void addSubtaskId(int subtaskId) { if (!subtaskIds.contains(subtaskId)) subtaskIds.add(subtaskId); } - public void removeSubtaskId(int subtaskId) { subtaskIds.remove(Integer.valueOf(subtaskId)); } - public void clearSubtasks() { subtaskIds.clear(); } + public List getSubtaskIds() { + return new ArrayList<>(subtaskIds); + } + + public void addSubtaskId(int subtaskId) { + if (!subtaskIds.contains(subtaskId)) { + subtaskIds.add(subtaskId); + } + } + + public void removeSubtaskId(int subtaskId) { + subtaskIds.remove(Integer.valueOf(subtaskId)); + } + + public void clearSubtasks() { + subtaskIds.clear(); + } @Override public void setStatus(Status status) { throw new UnsupportedOperationException("Статус эпика рассчитывается автоматически"); } - public void updateStatus(Status status) { this.status = status; } + public void updateStatus(Status status) { + this.status = status; + } @Override public String toString() { diff --git a/src/model/Subtask.java b/src/model/Subtask.java index d1547d7..6f089fc 100644 --- a/src/model/Subtask.java +++ b/src/model/Subtask.java @@ -13,8 +13,13 @@ public Subtask(String name, String description, int id, Status status, int epicI this.epicId = epicId; } - public int getEpicId() { return epicId; } - public void setEpicId(int epicId) { this.epicId = epicId; } + public int getEpicId() { + return epicId; + } + + public void setEpicId(int epicId) { + this.epicId = epicId; + } @Override public String toString() { diff --git a/src/model/Task.java b/src/model/Task.java index 1547b36..9754510 100644 --- a/src/model/Task.java +++ b/src/model/Task.java @@ -21,14 +21,37 @@ public Task(String name, String description, int id, Status status) { this.status = status; } - public String getName() { return name; } - public void setName(String name) { this.name = name; } - public String getDescription() { return description; } - public void setDescription(String description) { this.description = description; } - public int getId() { return id; } - public void setId(int id) { this.id = id; } - public Status getStatus() { return status; } - public void setStatus(Status status) { this.status = status; } + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } @Override public boolean equals(Object o) { @@ -39,7 +62,9 @@ public boolean equals(Object o) { } @Override - public int hashCode() { return Objects.hash(id); } + public int hashCode() { + return Objects.hash(id); + } @Override public String toString() { From aae9e83f396a8d5c69cab3c14fbf28b9a806a41d Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:02:40 +0300 Subject: [PATCH 03/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20Checkstyle:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BF=D1=83=D1=81=D1=82=D1=8B=D0=B5=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=BE=D0=BA=D0=B8=20=D0=BC=D0=B5=D0=B6=D0=B4=D1=83?= =?UTF-8?q?=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/HistoryManager.java | 2 ++ src/manager/TaskManager.java | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/manager/HistoryManager.java b/src/manager/HistoryManager.java index 079236c..1aa53c9 100644 --- a/src/manager/HistoryManager.java +++ b/src/manager/HistoryManager.java @@ -5,6 +5,8 @@ public interface HistoryManager { void add(Task task); + void remove(int id); + List getHistory(); } \ No newline at end of file diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java index 09e55fa..39943b0 100644 --- a/src/manager/TaskManager.java +++ b/src/manager/TaskManager.java @@ -7,28 +7,42 @@ public interface TaskManager { List getAllTasks(); + void deleteAllTasks(); + Task getTaskById(int id); + Task createTask(Task task); + void updateTask(Task task); + void deleteTaskById(int id); List getAllEpics(); + void deleteAllEpics(); + Epic getEpicById(int id); + Epic createEpic(Epic epic); + void updateEpic(Epic epic); + void deleteEpicById(int id); List getAllSubtasks(); + void deleteAllSubtasks(); + Subtask getSubtaskById(int id); + Subtask createSubtask(Subtask subtask); + void updateSubtask(Subtask subtask); + void deleteSubtaskById(int id); List getSubtasksByEpicId(int epicId); - // Новый метод для получения истории List getHistory(); } \ No newline at end of file From bda50775e9103ea6dca3b740d1bb24a72db77a9f Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:05:41 +0300 Subject: [PATCH 04/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20Checkstyle:=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=84=D0=B8=D0=B3?= =?UTF-8?q?=D1=83=D1=80=D0=BD=D1=8B=D1=85=20=D1=81=D0=BA=D0=BE=D0=B1=D0=BE?= =?UTF-8?q?=D0=BA=20=D0=B2=20=D1=83=D1=81=D0=BB=D0=BE=D0=B2=D0=B8=D1=8F?= =?UTF-8?q?=D1=85=20if?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/InMemoryTaskManager.java | 204 +++++++-------------------- 1 file changed, 49 insertions(+), 155 deletions(-) diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index a84675f..aee63da 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -17,9 +17,11 @@ public InMemoryTaskManager() { this.historyManager = Managers.getDefaultHistory(); } - private int generateId() { return nextId++; } + private int generateId() { + return nextId++; + } - // Методы получения задач с добавлением в историю + // Строка 20 - исправленный метод @Override public Task getTaskById(int id) { Task task = tasks.get(id); @@ -29,6 +31,8 @@ public Task getTaskById(int id) { return task; } + // Остальные методы get...ById тоже нужно проверить на аналогичные ошибки + @Override public Epic getEpicById(int id) { Epic epic = epics.get(id); @@ -47,130 +51,37 @@ public Subtask getSubtaskById(int id) { return subtask; } - @Override - public List getHistory() { - return historyManager.getHistory(); - } - - // Модифицируем методы удаления для очистки истории - @Override - public void deleteTaskById(int id) { - tasks.remove(id); - historyManager.remove(id); - } - - @Override - public void deleteEpicById(int id) { - Epic epic = epics.get(id); - if (epic != null) { - // Удаляем эпик и все его подзадачи из истории - historyManager.remove(id); - for (Integer subtaskId : epic.getSubtaskIds()) { - subtasks.remove(subtaskId); - historyManager.remove(subtaskId); - } - epics.remove(id); - } - } - - @Override - public void deleteSubtaskById(int id) { - Subtask subtask = subtasks.get(id); - if (subtask != null) { - Epic epic = epics.get(subtask.getEpicId()); - if (epic != null) { - epic.removeSubtaskId(id); - updateEpicStatus(epic.getId()); - } - subtasks.remove(id); - historyManager.remove(id); - } - } - - @Override - public void deleteAllTasks() { - for (Integer taskId : tasks.keySet()) { - historyManager.remove(taskId); - } - tasks.clear(); - } - - @Override - public void deleteAllEpics() { - // Удаляем все эпики и подзадачи из истории - for (Integer epicId : epics.keySet()) { - historyManager.remove(epicId); - } - for (Integer subtaskId : subtasks.keySet()) { - historyManager.remove(subtaskId); - } - - for (Epic epic : epics.values()) { - for (Integer subtaskId : epic.getSubtaskIds()) { - subtasks.remove(subtaskId); - } - } - epics.clear(); - } - - @Override - public void deleteAllSubtasks() { - // Удаляем все подзадачи из истории - for (Integer subtaskId : subtasks.keySet()) { - historyManager.remove(subtaskId); - } - - for (Epic epic : epics.values()) { - epic.clearSubtasks(); - updateEpicStatus(epic.getId()); - } - subtasks.clear(); - } - - // Остальные методы остаются без изменений... - @Override public List getAllTasks() { return new ArrayList<>(tasks.values()); } - + // Строка 131 - исправленный метод createTask @Override public Task createTask(Task task) { - if (task == null) return null; + if (task == null) { + return null; + } Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); tasks.put(newTask.getId(), newTask); return newTask; } - @Override - public void updateTask(Task task) { - if (task == null || !tasks.containsKey(task.getId())) return; - tasks.put(task.getId(), task); - } - - @Override - public List getAllEpics() { return new ArrayList<>(epics.values()); } - + // Строка 148 - исправленный метод createEpic @Override public Epic createEpic(Epic epic) { - if (epic == null) return null; + if (epic == null) { + return null; + } Epic newEpic = new Epic(epic.getName(), epic.getDescription()); newEpic.setId(generateId()); epics.put(newEpic.getId(), newEpic); return newEpic; } - @Override - public void updateEpic(Epic epic) { - if (epic == null || !epics.containsKey(epic.getId())) return; - Epic existingEpic = epics.get(epic.getId()); - existingEpic.setName(epic.getName()); - existingEpic.setDescription(epic.getDescription()); - } - - @Override - public List getAllSubtasks() { return new ArrayList<>(subtasks.values()); } - + // Строка 168 - исправленный метод createSubtask @Override public Subtask createSubtask(Subtask subtask) { - if (subtask == null || !epics.containsKey(subtask.getEpicId())) return null; - Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), generateId(), subtask.getStatus(), subtask.getEpicId()); + if (subtask == null || !epics.containsKey(subtask.getEpicId())) { + return null; + } + Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), + generateId(), subtask.getStatus(), subtask.getEpicId()); subtasks.put(newSubtask.getId(), newSubtask); Epic epic = epics.get(newSubtask.getEpicId()); epic.addSubtaskId(newSubtask.getId()); @@ -178,61 +89,44 @@ public Subtask createSubtask(Subtask subtask) { return newSubtask; } + // Также проверь другие методы с условиями if - они тоже могут требовать переноса + @Override - public void updateSubtask(Subtask subtask) { - if (subtask == null || !subtasks.containsKey(subtask.getId())) return; - Subtask existingSubtask = subtasks.get(subtask.getId()); - int oldEpicId = existingSubtask.getEpicId(); - int newEpicId = subtask.getEpicId(); - - if (oldEpicId != newEpicId) { - Epic oldEpic = epics.get(oldEpicId); - if (oldEpic != null) { - oldEpic.removeSubtaskId(subtask.getId()); - updateEpicStatus(oldEpicId); - } - Epic newEpic = epics.get(newEpicId); - if (newEpic != null) { - newEpic.addSubtaskId(subtask.getId()); - } + public void updateTask(Task task) { + if (task == null || !tasks.containsKey(task.getId())) { + return; } - - subtasks.put(subtask.getId(), subtask); - updateEpicStatus(subtask.getEpicId()); + tasks.put(task.getId(), task); } @Override - public List getSubtasksByEpicId(int epicId) { - List result = new ArrayList<>(); - Epic epic = epics.get(epicId); - if (epic != null) { - for (Integer subtaskId : epic.getSubtaskIds()) { - Subtask subtask = subtasks.get(subtaskId); - if (subtask != null) result.add(subtask); - } + public void updateEpic(Epic epic) { + if (epic == null || !epics.containsKey(epic.getId())) { + return; } - return result; + Epic existingEpic = epics.get(epic.getId()); + existingEpic.setName(epic.getName()); + existingEpic.setDescription(epic.getDescription()); } - private void updateEpicStatus(int epicId) { - Epic epic = epics.get(epicId); - if (epic == null) return; - List subtaskIds = epic.getSubtaskIds(); - if (subtaskIds.isEmpty()) { - epic.updateStatus(Status.NEW); + @Override + public void updateSubtask(Subtask subtask) { + if (subtask == null || !subtasks.containsKey(subtask.getId())) { return; } - boolean allDone = true; - boolean allNew = true; - for (Integer subtaskId : subtaskIds) { - Subtask subtask = subtasks.get(subtaskId); - if (subtask == null) continue; - if (subtask.getStatus() != Status.DONE) allDone = false; - if (subtask.getStatus() != Status.NEW) allNew = false; - if (!allDone && !allNew) break; - } - if (allDone) epic.updateStatus(Status.DONE); - else if (allNew) epic.updateStatus(Status.NEW); - else epic.updateStatus(Status.IN_PROGRESS); + // ... остальная логика метода } + + // Остальные методы остаются без изменений... + @Override + public List getAllTasks() { + return new ArrayList<>(tasks.values()); + } + + @Override + public void deleteAllTasks() { + tasks.clear(); + } + + // ... и т.д. } \ No newline at end of file From 77008b59273f1c3a2f25248a5cff06d8431c05e1 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:10:28 +0300 Subject: [PATCH 05/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B2=D1=81=D0=B5=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B8=20Checkstyle=20=D0=B2=20InMemoryTaskMa?= =?UTF-8?q?nager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/InMemoryTaskManager.java | 227 +++++++++++++++++++++------ 1 file changed, 182 insertions(+), 45 deletions(-) diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index aee63da..1aeac46 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -3,7 +3,6 @@ import model.Task; import model.Epic; import model.Subtask; -import model.Status; import java.util.*; public class InMemoryTaskManager implements TaskManager { @@ -21,7 +20,19 @@ private int generateId() { return nextId++; } - // Строка 20 - исправленный метод + @Override + public List getAllTasks() { + return new ArrayList<>(tasks.values()); + } + + @Override + public void deleteAllTasks() { + for (Integer taskId : tasks.keySet()) { + historyManager.remove(taskId); + } + tasks.clear(); + } + @Override public Task getTaskById(int id) { Task task = tasks.get(id); @@ -31,38 +42,61 @@ public Task getTaskById(int id) { return task; } - // Остальные методы get...ById тоже нужно проверить на аналогичные ошибки + @Override + public Task createTask(Task task) { + if (task == null) { + return null; + } + Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); + tasks.put(newTask.getId(), newTask); + return newTask; + } @Override - public Epic getEpicById(int id) { - Epic epic = epics.get(id); - if (epic != null) { - historyManager.add(epic); + public void updateTask(Task task) { + if (task == null || !tasks.containsKey(task.getId())) { + return; } - return epic; + tasks.put(task.getId(), task); } @Override - public Subtask getSubtaskById(int id) { - Subtask subtask = subtasks.get(id); - if (subtask != null) { - historyManager.add(subtask); + public void deleteTaskById(int id) { + tasks.remove(id); + historyManager.remove(id); + } + + @Override + public List getAllEpics() { + return new ArrayList<>(epics.values()); + } + + @Override + public void deleteAllEpics() { + for (Integer epicId : epics.keySet()) { + historyManager.remove(epicId); } - return subtask; + for (Integer subtaskId : subtasks.keySet()) { + historyManager.remove(subtaskId); + } + + for (Epic epic : epics.values()) { + for (Integer subtaskId : epic.getSubtaskIds()) { + subtasks.remove(subtaskId); + } + } + epics.clear(); } - // Строка 131 - исправленный метод createTask @Override - public Task createTask(Task task) { - if (task == null) { - return null; + public Epic getEpicById(int id) { + Epic epic = epics.get(id); + if (epic != null) { + historyManager.add(epic); } - Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); - tasks.put(newTask.getId(), newTask); - return newTask; + return epic; } - // Строка 148 - исправленный метод createEpic @Override public Epic createEpic(Epic epic) { if (epic == null) { @@ -74,7 +108,56 @@ public Epic createEpic(Epic epic) { return newEpic; } - // Строка 168 - исправленный метод createSubtask + @Override + public void updateEpic(Epic epic) { + if (epic == null || !epics.containsKey(epic.getId())) { + return; + } + Epic existingEpic = epics.get(epic.getId()); + existingEpic.setName(epic.getName()); + existingEpic.setDescription(epic.getDescription()); + } + + @Override + public void deleteEpicById(int id) { + Epic epic = epics.get(id); + if (epic != null) { + historyManager.remove(id); + for (Integer subtaskId : epic.getSubtaskIds()) { + subtasks.remove(subtaskId); + historyManager.remove(subtaskId); + } + epics.remove(id); + } + } + + @Override + public List getAllSubtasks() { + return new ArrayList<>(subtasks.values()); + } + + @Override + public void deleteAllSubtasks() { + for (Integer subtaskId : subtasks.keySet()) { + historyManager.remove(subtaskId); + } + + for (Epic epic : epics.values()) { + epic.clearSubtasks(); + updateEpicStatus(epic.getId()); + } + subtasks.clear(); + } + + @Override + public Subtask getSubtaskById(int id) { + Subtask subtask = subtasks.get(id); + if (subtask != null) { + historyManager.add(subtask); + } + return subtask; + } + @Override public Subtask createSubtask(Subtask subtask) { if (subtask == null || !epics.containsKey(subtask.getEpicId())) { @@ -89,44 +172,98 @@ public Subtask createSubtask(Subtask subtask) { return newSubtask; } - // Также проверь другие методы с условиями if - они тоже могут требовать переноса - @Override - public void updateTask(Task task) { - if (task == null || !tasks.containsKey(task.getId())) { + public void updateSubtask(Subtask subtask) { + if (subtask == null || !subtasks.containsKey(subtask.getId())) { return; } - tasks.put(task.getId(), task); - } + Subtask existingSubtask = subtasks.get(subtask.getId()); + int oldEpicId = existingSubtask.getEpicId(); + int newEpicId = subtask.getEpicId(); - @Override - public void updateEpic(Epic epic) { - if (epic == null || !epics.containsKey(epic.getId())) { - return; + if (oldEpicId != newEpicId) { + Epic oldEpic = epics.get(oldEpicId); + if (oldEpic != null) { + oldEpic.removeSubtaskId(subtask.getId()); + updateEpicStatus(oldEpicId); + } + Epic newEpic = epics.get(newEpicId); + if (newEpic != null) { + newEpic.addSubtaskId(subtask.getId()); + } } - Epic existingEpic = epics.get(epic.getId()); - existingEpic.setName(epic.getName()); - existingEpic.setDescription(epic.getDescription()); + + subtasks.put(subtask.getId(), subtask); + updateEpicStatus(subtask.getEpicId()); } @Override - public void updateSubtask(Subtask subtask) { - if (subtask == null || !subtasks.containsKey(subtask.getId())) { - return; + public void deleteSubtaskById(int id) { + Subtask subtask = subtasks.get(id); + if (subtask != null) { + Epic epic = epics.get(subtask.getEpicId()); + if (epic != null) { + epic.removeSubtaskId(id); + updateEpicStatus(epic.getId()); + } + subtasks.remove(id); + historyManager.remove(id); } - // ... остальная логика метода } - // Остальные методы остаются без изменений... @Override - public List getAllTasks() { - return new ArrayList<>(tasks.values()); + public List getSubtasksByEpicId(int epicId) { + List result = new ArrayList<>(); + Epic epic = epics.get(epicId); + if (epic != null) { + for (Integer subtaskId : epic.getSubtaskIds()) { + Subtask subtask = subtasks.get(subtaskId); + if (subtask != null) { + result.add(subtask); + } + } + } + return result; } @Override - public void deleteAllTasks() { - tasks.clear(); + public List getHistory() { + return historyManager.getHistory(); } - // ... и т.д. + private void updateEpicStatus(int epicId) { + Epic epic = epics.get(epicId); + if (epic == null) { + return; + } + List subtaskIds = epic.getSubtaskIds(); + if (subtaskIds.isEmpty()) { + epic.updateStatus(model.Status.NEW); + return; + } + boolean allDone = true; + boolean allNew = true; + for (Integer subtaskId : subtaskIds) { + Subtask subtask = subtasks.get(subtaskId); + if (subtask == null) { + continue; + } + if (subtask.getStatus() != model.Status.DONE) { + allDone = false; + } + if (subtask.getStatus() != model.Status.NEW) { + allNew = false; + } + if (!allDone && !allNew) { + break; + } + } + if (allDone) { + epic.updateStatus(model.Status.DONE); + } else if (allNew) { + epic.updateStatus(model.Status.NEW); + } else { + epic.updateStatus(model.Status.IN_PROGRESS); + } + } } \ No newline at end of file From 4fe829fefccd85c057778e5fbcce0794067c4a76 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:25:52 +0300 Subject: [PATCH 06/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/InMemoryHistoryManager.java | 38 ++- src/manager/InMemoryTaskManager.java | 2 + test/manager/InMemoryTaskManagerTest.java | 354 ++++++++++++++++++++-- 3 files changed, 364 insertions(+), 30 deletions(-) diff --git a/src/manager/InMemoryHistoryManager.java b/src/manager/InMemoryHistoryManager.java index c9b1a5b..bf7c45b 100644 --- a/src/manager/InMemoryHistoryManager.java +++ b/src/manager/InMemoryHistoryManager.java @@ -1,6 +1,8 @@ package manager; import model.Task; +import model.Epic; +import model.Subtask; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -25,14 +27,36 @@ private static class Node { public void add(Task task) { if (task == null) return; - // Удаляем существующую задачу из истории, если она есть remove(task.getId()); - // Добавляем задачу в конец списка - Node newNode = linkLast(task); - historyMap.put(task.getId(), newNode); + + Task taskCopy = copyTask(task); + Node newNode = linkLast(taskCopy); + historyMap.put(taskCopy.getId(), newNode); + } + + private Task copyTask(Task original) { + if (original instanceof Epic) { + Epic epic = (Epic) original; + Epic copy = new Epic(epic.getName(), epic.getDescription()); + copy.setId(epic.getId()); + copy.updateStatus(epic.getStatus()); + // Копируем список подзадач + for (Integer subtaskId : epic.getSubtaskIds()) { + copy.addSubtaskId(subtaskId); + } + return copy; + } else if (original instanceof Subtask) { + Subtask subtask = (Subtask) original; + return new Subtask(subtask.getName(), subtask.getDescription(), + subtask.getId(), subtask.getStatus(), subtask.getEpicId()); + } else { + return new Task(original.getName(), original.getDescription(), + original.getId(), original.getStatus()); + } } + @Override public void remove(int id) { Node node = historyMap.get(id); @@ -51,11 +75,9 @@ private Node linkLast(Task task) { Node newNode = new Node(task); if (tail == null) { - // Список пустой head = newNode; tail = newNode; } else { - // Добавляем в конец списка tail.next = newNode; newNode.prev = tail; tail = newNode; @@ -67,22 +89,18 @@ private Node linkLast(Task task) { private void removeNode(Node node) { if (node == null) return; - // Обновляем ссылки соседних узлов if (node.prev != null) { node.prev.next = node.next; } else { - // Удаляем голову head = node.next; } if (node.next != null) { node.next.prev = node.prev; } else { - // Удаляем хвост tail = node.prev; } - // Очищаем ссылки удаляемого узла node.prev = null; node.next = null; } diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 1aeac46..12bb816 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -163,6 +163,7 @@ public Subtask createSubtask(Subtask subtask) { if (subtask == null || !epics.containsKey(subtask.getEpicId())) { return null; } + Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), generateId(), subtask.getStatus(), subtask.getEpicId()); subtasks.put(newSubtask.getId(), newSubtask); @@ -177,6 +178,7 @@ public void updateSubtask(Subtask subtask) { if (subtask == null || !subtasks.containsKey(subtask.getId())) { return; } + Subtask existingSubtask = subtasks.get(subtask.getId()); int oldEpicId = existingSubtask.getEpicId(); int newEpicId = subtask.getEpicId(); diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java index 2417438..80905f0 100644 --- a/test/manager/InMemoryTaskManagerTest.java +++ b/test/manager/InMemoryTaskManagerTest.java @@ -18,25 +18,256 @@ void setUp() { taskManager = new InMemoryTaskManager(); } + @Test + void testCreateAndGetTask() { + Task task = new Task("Test Task", "Test Description", Status.NEW); + Task createdTask = taskManager.createTask(task); + + assertNotNull(createdTask); + assertNotNull(createdTask.getId()); + assertEquals("Test Task", createdTask.getName()); + assertEquals("Test Description", createdTask.getDescription()); + assertEquals(Status.NEW, createdTask.getStatus()); + + Task retrievedTask = taskManager.getTaskById(createdTask.getId()); + assertEquals(createdTask, retrievedTask); + } + + @Test + void testCreateAndGetEpic() { + Epic epic = new Epic("Test Epic", "Test Epic Description"); + Epic createdEpic = taskManager.createEpic(epic); + + assertNotNull(createdEpic); + assertNotNull(createdEpic.getId()); + assertEquals("Test Epic", createdEpic.getName()); + assertEquals("Test Epic Description", createdEpic.getDescription()); + assertEquals(Status.NEW, createdEpic.getStatus()); + assertTrue(createdEpic.getSubtaskIds().isEmpty()); + + Epic retrievedEpic = taskManager.getEpicById(createdEpic.getId()); + assertEquals(createdEpic, retrievedEpic); + } + + @Test + void testCreateAndGetSubtask() { + Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); + Subtask subtask = new Subtask("Test Subtask", "Test Description", Status.NEW, epic.getId()); + Subtask createdSubtask = taskManager.createSubtask(subtask); + + assertNotNull(createdSubtask); + assertNotNull(createdSubtask.getId()); + assertEquals("Test Subtask", createdSubtask.getName()); + assertEquals("Test Description", createdSubtask.getDescription()); + assertEquals(Status.NEW, createdSubtask.getStatus()); + assertEquals(epic.getId(), createdSubtask.getEpicId()); + + Subtask retrievedSubtask = taskManager.getSubtaskById(createdSubtask.getId()); + assertEquals(createdSubtask, retrievedSubtask); + + List epicSubtasks = taskManager.getSubtasksByEpicId(epic.getId()); + assertEquals(1, epicSubtasks.size()); + assertEquals(createdSubtask.getId(), epicSubtasks.get(0).getId()); + } + + @Test + void testUpdateTask() { + Task task = taskManager.createTask(new Task("Original", "Desc", Status.NEW)); + Task updatedTask = new Task("Updated", "Updated Desc", task.getId(), Status.IN_PROGRESS); + + taskManager.updateTask(updatedTask); + + Task retrievedTask = taskManager.getTaskById(task.getId()); + assertEquals("Updated", retrievedTask.getName()); + assertEquals("Updated Desc", retrievedTask.getDescription()); + assertEquals(Status.IN_PROGRESS, retrievedTask.getStatus()); + } + + @Test + void testUpdateEpic() { + Epic epic = taskManager.createEpic(new Epic("Original", "Desc")); + Epic updatedEpic = new Epic("Updated", "Updated Desc"); + updatedEpic.setId(epic.getId()); + + taskManager.updateEpic(updatedEpic); + + Epic retrievedEpic = taskManager.getEpicById(epic.getId()); + assertEquals("Updated", retrievedEpic.getName()); + assertEquals("Updated Desc", retrievedEpic.getDescription()); + } + + @Test + void testUpdateSubtask() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + Subtask subtask = taskManager.createSubtask(new Subtask("Original", "Desc", Status.NEW, epic.getId())); + Subtask updatedSubtask = new Subtask("Updated", "Updated Desc", subtask.getId(), Status.DONE, epic.getId()); + + taskManager.updateSubtask(updatedSubtask); + + Subtask retrievedSubtask = taskManager.getSubtaskById(subtask.getId()); + assertEquals("Updated", retrievedSubtask.getName()); + assertEquals("Updated Desc", retrievedSubtask.getDescription()); + assertEquals(Status.DONE, retrievedSubtask.getStatus()); + } + + @Test + void testDeleteTask() { + Task task = taskManager.createTask(new Task("Test", "Desc", Status.NEW)); + int taskId = task.getId(); + + taskManager.deleteTaskById(taskId); + + assertNull(taskManager.getTaskById(taskId)); + assertTrue(taskManager.getAllTasks().isEmpty()); + } + + @Test + void testDeleteEpic() { + Epic epic = taskManager.createEpic(new Epic("Test", "Desc")); + int epicId = epic.getId(); + + taskManager.deleteEpicById(epicId); + + assertNull(taskManager.getEpicById(epicId)); + assertTrue(taskManager.getAllEpics().isEmpty()); + } + + @Test + void testDeleteSubtask() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + Subtask subtask = taskManager.createSubtask(new Subtask("Test", "Desc", Status.NEW, epic.getId())); + int subtaskId = subtask.getId(); + + taskManager.deleteSubtaskById(subtaskId); + + assertNull(taskManager.getSubtaskById(subtaskId)); + assertTrue(taskManager.getAllSubtasks().isEmpty()); + assertTrue(epic.getSubtaskIds().isEmpty()); + } + + @Test + void testDeleteAllTasks() { + taskManager.createTask(new Task("Task 1", "Desc", Status.NEW)); + taskManager.createTask(new Task("Task 2", "Desc", Status.IN_PROGRESS)); + + taskManager.deleteAllTasks(); + + assertTrue(taskManager.getAllTasks().isEmpty()); + } + + @Test + void testDeleteAllEpics() { + taskManager.createEpic(new Epic("Epic 1", "Desc")); + taskManager.createEpic(new Epic("Epic 2", "Desc")); + + taskManager.deleteAllEpics(); + + assertTrue(taskManager.getAllEpics().isEmpty()); + assertTrue(taskManager.getAllSubtasks().isEmpty()); + } + + @Test + void testDeleteAllSubtasks() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + taskManager.createSubtask(new Subtask("Sub 1", "Desc", Status.NEW, epic.getId())); + taskManager.createSubtask(new Subtask("Sub 2", "Desc", Status.DONE, epic.getId())); + + taskManager.deleteAllSubtasks(); + + assertTrue(taskManager.getAllSubtasks().isEmpty()); + assertTrue(epic.getSubtaskIds().isEmpty()); + } + + @Test + void testGetAllTasks() { + Task task1 = taskManager.createTask(new Task("Task 1", "Desc", Status.NEW)); + Task task2 = taskManager.createTask(new Task("Task 2", "Desc", Status.IN_PROGRESS)); + + List allTasks = taskManager.getAllTasks(); + + assertEquals(2, allTasks.size()); + assertTrue(allTasks.contains(task1)); + assertTrue(allTasks.contains(task2)); + } + + @Test + void testGetAllEpics() { + Epic epic1 = taskManager.createEpic(new Epic("Epic 1", "Desc")); + Epic epic2 = taskManager.createEpic(new Epic("Epic 2", "Desc")); + + List allEpics = taskManager.getAllEpics(); + + assertEquals(2, allEpics.size()); + assertTrue(allEpics.contains(epic1)); + assertTrue(allEpics.contains(epic2)); + } + + @Test + void testGetAllSubtasks() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + Subtask subtask1 = taskManager.createSubtask(new Subtask("Sub 1", "Desc", Status.NEW, epic.getId())); + Subtask subtask2 = taskManager.createSubtask(new Subtask("Sub 2", "Desc", Status.DONE, epic.getId())); + + List allSubtasks = taskManager.getAllSubtasks(); + + assertEquals(2, allSubtasks.size()); + assertTrue(allSubtasks.contains(subtask1)); + assertTrue(allSubtasks.contains(subtask2)); + } + + @Test + void testEpicStatusCalculation() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + + // Пустой эпик - NEW + assertEquals(Status.NEW, epic.getStatus()); + + // Все подзадачи NEW - эпик NEW + Subtask sub1 = taskManager.createSubtask(new Subtask("Sub 1", "Desc", Status.NEW, epic.getId())); + assertEquals(Status.NEW, epic.getStatus()); + + // Все подзадачи DONE - эпик DONE + taskManager.updateSubtask(new Subtask("Sub 1", "Desc", sub1.getId(), Status.DONE, epic.getId())); + assertEquals(Status.DONE, epic.getStatus()); + + // Подзадачи NEW и DONE - эпик IN_PROGRESS + Subtask sub2 = taskManager.createSubtask(new Subtask("Sub 2", "Desc", Status.NEW, epic.getId())); + assertEquals(Status.IN_PROGRESS, epic.getStatus()); + + // Все подзадачи IN_PROGRESS - эпик IN_PROGRESS + taskManager.updateSubtask(new Subtask("Sub 1", "Desc", sub1.getId(), Status.IN_PROGRESS, epic.getId())); + taskManager.updateSubtask(new Subtask("Sub 2", "Desc", sub2.getId(), Status.IN_PROGRESS, epic.getId())); + assertEquals(Status.IN_PROGRESS, epic.getStatus()); + } + + @Test + void testHistoryWithoutDuplicates() { + Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); + + // Многократно запрашиваем одну и ту же задачу + taskManager.getTaskById(task.getId()); + taskManager.getTaskById(task.getId()); + taskManager.getTaskById(task.getId()); + + // В истории должна быть только одна запись + assertEquals(1, taskManager.getHistory().size()); + } + @Test void testTaskImmutability() { - // Создаем задачу через менеджер Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); int originalId = task.getId(); - String originalName = task.getName(); // Пытаемся изменить задачу через сеттеры task.setId(999); task.setName("Modified"); - task.setStatus(Status.DONE); // Получаем задачу из менеджера Task managedTask = taskManager.getTaskById(originalId); // Проверяем, что данные в менеджере не изменились assertEquals(originalId, managedTask.getId()); - assertEquals(originalName, managedTask.getName()); - assertEquals(Status.NEW, managedTask.getStatus()); + assertEquals("Test", managedTask.getName()); } @Test @@ -57,16 +288,18 @@ void testEpicImmutability() { } @Test - void testHistoryWithoutDuplicates() { + void testEmptyHistoryAfterTaskDeletion() { Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); - // Многократно запрашиваем одну и ту же задачу - taskManager.getTaskById(task.getId()); - taskManager.getTaskById(task.getId()); + // Добавляем в историю taskManager.getTaskById(task.getId()); - - // В истории должна быть только одна запись assertEquals(1, taskManager.getHistory().size()); + + // Удаляем задачу + taskManager.deleteTaskById(task.getId()); + + // История должна быть пустой + assertTrue(taskManager.getHistory().isEmpty()); } @Test @@ -89,17 +322,98 @@ void testDataIntegrityAfterDeletion() { } @Test - void testEmptyHistoryAfterTaskDeletion() { - Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); + void testCreateTaskWithNull() { + Task result = taskManager.createTask(null); + assertNull(result); + } - // Добавляем в историю - taskManager.getTaskById(task.getId()); - assertEquals(1, taskManager.getHistory().size()); + @Test + void testCreateEpicWithNull() { + Epic result = taskManager.createEpic(null); + assertNull(result); + } - // Удаляем задачу - taskManager.deleteTaskById(task.getId()); + @Test + void testCreateSubtaskWithNull() { + Subtask result = taskManager.createSubtask(null); + assertNull(result); + } - // История должна быть пустой - assertTrue(taskManager.getHistory().isEmpty()); + @Test + void testCreateSubtaskWithInvalidEpic() { + Subtask subtask = new Subtask("Test", "Desc", Status.NEW, 999); + Subtask result = taskManager.createSubtask(subtask); + assertNull(result); + } + + @Test + void testUpdateNonExistentTask() { + Task task = new Task("Test", "Desc", 999, Status.NEW); + taskManager.updateTask(task); + + // Не должно быть исключений + assertNull(taskManager.getTaskById(999)); + } + + @Test + void testUpdateNonExistentEpic() { + Epic epic = new Epic("Test", "Desc"); + epic.setId(999); + taskManager.updateEpic(epic); + + // Не должно быть исключений + assertNull(taskManager.getEpicById(999)); + } + + @Test + void testUpdateNonExistentSubtask() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); + Subtask subtask = new Subtask("Test", "Desc", 999, Status.NEW, epic.getId()); + taskManager.updateSubtask(subtask); + + // Не должно быть исключений + assertNull(taskManager.getSubtaskById(999)); + } + + @Test + void testDeleteNonExistentTask() { + taskManager.deleteTaskById(999); + // Не должно быть исключений + } + + @Test + void testDeleteNonExistentEpic() { + taskManager.deleteEpicById(999); + // Не должно быть исключений + } + + @Test + void testDeleteNonExistentSubtask() { + taskManager.deleteSubtaskById(999); + // Не должно быть исключений + } + + @Test + void testGetNonExistentTask() { + Task task = taskManager.getTaskById(999); + assertNull(task); + } + + @Test + void testGetNonExistentEpic() { + Epic epic = taskManager.getEpicById(999); + assertNull(epic); + } + + @Test + void testGetNonExistentSubtask() { + Subtask subtask = taskManager.getSubtaskById(999); + assertNull(subtask); + } + + @Test + void testGetSubtasksByNonExistentEpic() { + List subtasks = taskManager.getSubtasksByEpicId(999); + assertTrue(subtasks.isEmpty()); } } \ No newline at end of file From 4a2d1379e16ab14974177e23c49a5c9b15bc5358 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:43:34 +0300 Subject: [PATCH 07/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B2=D1=81=D0=B5=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B8=20=D0=B2=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=8A=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/InMemoryTaskManager.java | 70 ++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 10 deletions(-) diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 12bb816..b20dcaf 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -20,9 +20,33 @@ private int generateId() { return nextId++; } + private Task createTaskCopy(Task original) { + return new Task(original.getName(), original.getDescription(), + original.getId(), original.getStatus()); + } + + private Epic createEpicCopy(Epic original) { + Epic copy = new Epic(original.getName(), original.getDescription()); + copy.setId(original.getId()); + copy.updateStatus(original.getStatus()); + for (Integer subtaskId : original.getSubtaskIds()) { + copy.addSubtaskId(subtaskId); + } + return copy; + } + + private Subtask createSubtaskCopy(Subtask original) { + return new Subtask(original.getName(), original.getDescription(), + original.getId(), original.getStatus(), original.getEpicId()); + } + @Override public List getAllTasks() { - return new ArrayList<>(tasks.values()); + List taskCopies = new ArrayList<>(); + for (Task task : tasks.values()) { + taskCopies.add(createTaskCopy(task)); + } + return taskCopies; } @Override @@ -38,8 +62,9 @@ public Task getTaskById(int id) { Task task = tasks.get(id); if (task != null) { historyManager.add(task); + return createTaskCopy(task); } - return task; + return null; } @Override @@ -49,7 +74,7 @@ public Task createTask(Task task) { } Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); tasks.put(newTask.getId(), newTask); - return newTask; + return createTaskCopy(newTask); } @Override @@ -68,7 +93,11 @@ public void deleteTaskById(int id) { @Override public List getAllEpics() { - return new ArrayList<>(epics.values()); + List epicCopies = new ArrayList<>(); + for (Epic epic : epics.values()) { + epicCopies.add(createEpicCopy(epic)); + } + return epicCopies; } @Override @@ -93,8 +122,9 @@ public Epic getEpicById(int id) { Epic epic = epics.get(id); if (epic != null) { historyManager.add(epic); + return createEpicCopy(epic); } - return epic; + return null; } @Override @@ -105,7 +135,7 @@ public Epic createEpic(Epic epic) { Epic newEpic = new Epic(epic.getName(), epic.getDescription()); newEpic.setId(generateId()); epics.put(newEpic.getId(), newEpic); - return newEpic; + return createEpicCopy(newEpic); } @Override @@ -133,7 +163,11 @@ public void deleteEpicById(int id) { @Override public List getAllSubtasks() { - return new ArrayList<>(subtasks.values()); + List subtaskCopies = new ArrayList<>(); + for (Subtask subtask : subtasks.values()) { + subtaskCopies.add(createSubtaskCopy(subtask)); + } + return subtaskCopies; } @Override @@ -154,8 +188,9 @@ public Subtask getSubtaskById(int id) { Subtask subtask = subtasks.get(id); if (subtask != null) { historyManager.add(subtask); + return createSubtaskCopy(subtask); } - return subtask; + return null; } @Override @@ -164,13 +199,18 @@ public Subtask createSubtask(Subtask subtask) { return null; } + // Проверка: подзадача не может быть своим же эпиком + if (subtask.getEpicId() == subtask.getId()) { + return null; + } + Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), generateId(), subtask.getStatus(), subtask.getEpicId()); subtasks.put(newSubtask.getId(), newSubtask); Epic epic = epics.get(newSubtask.getEpicId()); epic.addSubtaskId(newSubtask.getId()); updateEpicStatus(epic.getId()); - return newSubtask; + return createSubtaskCopy(newSubtask); } @Override @@ -179,6 +219,16 @@ public void updateSubtask(Subtask subtask) { return; } + // Проверка: подзадача не может быть своим же эпиком + if (subtask.getEpicId() == subtask.getId()) { + return; + } + + // Проверка: эпик должен существовать + if (!epics.containsKey(subtask.getEpicId())) { + return; + } + Subtask existingSubtask = subtasks.get(subtask.getId()); int oldEpicId = existingSubtask.getEpicId(); int newEpicId = subtask.getEpicId(); @@ -221,7 +271,7 @@ public List getSubtasksByEpicId(int epicId) { for (Integer subtaskId : epic.getSubtaskIds()) { Subtask subtask = subtasks.get(subtaskId); if (subtask != null) { - result.add(subtask); + result.add(createSubtaskCopy(subtask)); } } } From 4a7467ca3ea57ef682a9f96c038cd498b6b28093 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:49:55 +0300 Subject: [PATCH 08/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/InMemoryTaskManager.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index b20dcaf..2c2adb8 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -199,15 +199,21 @@ public Subtask createSubtask(Subtask subtask) { return null; } + Epic epic = epics.get(subtask.getEpicId()); + + // Проверка: эпик не может быть своей же подзадачей + if (epic != null && subtask.getId() != 0 && epic.getId() == subtask.getId()) { + return null; + } + // Проверка: подзадача не может быть своим же эпиком - if (subtask.getEpicId() == subtask.getId()) { + if (subtask.getId() != 0 && subtask.getEpicId() == subtask.getId()) { return null; } Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), generateId(), subtask.getStatus(), subtask.getEpicId()); subtasks.put(newSubtask.getId(), newSubtask); - Epic epic = epics.get(newSubtask.getEpicId()); epic.addSubtaskId(newSubtask.getId()); updateEpicStatus(epic.getId()); return createSubtaskCopy(newSubtask); @@ -246,6 +252,8 @@ public void updateSubtask(Subtask subtask) { } subtasks.put(subtask.getId(), subtask); + + // Всегда обновляем статус эпика после изменения подзадачи updateEpicStatus(subtask.getEpicId()); } From 212dfb1af02eaf416f642e5d6a2363d4f86ad0dd Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 21:58:17 +0300 Subject: [PATCH 09/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/InMemoryTaskManager.java | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 2c2adb8..7ce0480 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -201,14 +201,16 @@ public Subtask createSubtask(Subtask subtask) { Epic epic = epics.get(subtask.getEpicId()); - // Проверка: эпик не может быть своей же подзадачей - if (epic != null && subtask.getId() != 0 && epic.getId() == subtask.getId()) { - return null; - } - - // Проверка: подзадача не может быть своим же эпиком - if (subtask.getId() != 0 && subtask.getEpicId() == subtask.getId()) { - return null; + // Усиленная проверка циклических ссылок + if (subtask.getId() != 0) { + // Подзадача не может быть своим же эпиком + if (subtask.getEpicId() == subtask.getId()) { + return null; + } + // Эпик не может быть своей же подзадачей + if (epic.getId() == subtask.getId()) { + return null; + } } Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), @@ -251,9 +253,13 @@ public void updateSubtask(Subtask subtask) { } } - subtasks.put(subtask.getId(), subtask); + // Обновляем существующую подзадачу, а не заменяем ее + existingSubtask.setName(subtask.getName()); + existingSubtask.setDescription(subtask.getDescription()); + existingSubtask.setStatus(subtask.getStatus()); + existingSubtask.setEpicId(subtask.getEpicId()); - // Всегда обновляем статус эпика после изменения подзадачи + // Всегда обновляем статус эпика updateEpicStatus(subtask.getEpicId()); } From b9f95fb253b435d4586f550e5b8dc725b12657e3 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Tue, 7 Oct 2025 22:10:16 +0300 Subject: [PATCH 10/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=D0=B4=D0=BD=D0=B8=D1=85=20=D1=82=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= =?UTF-8?q?=20=D1=81=20=D1=8D=D0=BF=D0=B8=D0=BA=D0=B0=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/manager/InMemoryTaskManagerTest.java | 12 ++++++++---- test/model/EpicIntegrationTest.java | 6 +++--- test/model/SubtaskIntegrationTest.java | 11 +++++------ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java index 80905f0..89b2947 100644 --- a/test/manager/InMemoryTaskManagerTest.java +++ b/test/manager/InMemoryTaskManagerTest.java @@ -224,20 +224,24 @@ void testEpicStatusCalculation() { // Все подзадачи NEW - эпик NEW Subtask sub1 = taskManager.createSubtask(new Subtask("Sub 1", "Desc", Status.NEW, epic.getId())); - assertEquals(Status.NEW, epic.getStatus()); + Epic updatedEpic1 = taskManager.getEpicById(epic.getId()); + assertEquals(Status.NEW, updatedEpic1.getStatus()); // Все подзадачи DONE - эпик DONE taskManager.updateSubtask(new Subtask("Sub 1", "Desc", sub1.getId(), Status.DONE, epic.getId())); - assertEquals(Status.DONE, epic.getStatus()); + Epic updatedEpic2 = taskManager.getEpicById(epic.getId()); + assertEquals(Status.DONE, updatedEpic2.getStatus()); // Подзадачи NEW и DONE - эпик IN_PROGRESS Subtask sub2 = taskManager.createSubtask(new Subtask("Sub 2", "Desc", Status.NEW, epic.getId())); - assertEquals(Status.IN_PROGRESS, epic.getStatus()); + Epic updatedEpic3 = taskManager.getEpicById(epic.getId()); + assertEquals(Status.IN_PROGRESS, updatedEpic3.getStatus()); // Все подзадачи IN_PROGRESS - эпик IN_PROGRESS taskManager.updateSubtask(new Subtask("Sub 1", "Desc", sub1.getId(), Status.IN_PROGRESS, epic.getId())); taskManager.updateSubtask(new Subtask("Sub 2", "Desc", sub2.getId(), Status.IN_PROGRESS, epic.getId())); - assertEquals(Status.IN_PROGRESS, epic.getStatus()); + Epic updatedEpic4 = taskManager.getEpicById(epic.getId()); + assertEquals(Status.IN_PROGRESS, updatedEpic4.getStatus()); } @Test diff --git a/test/model/EpicIntegrationTest.java b/test/model/EpicIntegrationTest.java index 772bb13..dfeb69f 100644 --- a/test/model/EpicIntegrationTest.java +++ b/test/model/EpicIntegrationTest.java @@ -18,10 +18,10 @@ void setUp() { void epicCannotBeAddedAsItsOwnSubtask() { Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); - // Пытаемся создать подзадачу с epicId равным ID самого эпика - Subtask invalidSubtask = new Subtask("Invalid", "Description", Status.NEW, epic.getId()); + // Пытаемся создать подзадачу с epicId равным ID самого эпика и id равным epicId + Subtask invalidSubtask = new Subtask("Invalid", "Description", epic.getId(), Status.NEW, epic.getId()); - // Должен вернуть null, так как epic не может быть своим же эпиком + // Должен вернуть null, так как epic не может быть своей же подзадачей assertNull(taskManager.createSubtask(invalidSubtask), "Epic не должен быть добавлен в качестве своей же подзадачи"); } diff --git a/test/model/SubtaskIntegrationTest.java b/test/model/SubtaskIntegrationTest.java index 084c298..aff2157 100644 --- a/test/model/SubtaskIntegrationTest.java +++ b/test/model/SubtaskIntegrationTest.java @@ -19,12 +19,11 @@ void subtaskCannotBeItsOwnEpic() { Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); Subtask subtask = taskManager.createSubtask(new Subtask("Subtask", "Description", Status.NEW, epic.getId())); - // Пытаемся изменить epicId подзадачи на её собственный ID - subtask.setEpicId(subtask.getId()); - taskManager.updateSubtask(subtask); + // Создаем подзадачу, которая пытается быть своим же эпиком + Subtask invalidSubtask = new Subtask("Invalid", "Description", subtask.getId(), Status.NEW, subtask.getId()); - // Epic ID не должен измениться на собственный ID подзадачи - assertNotEquals(subtask.getId(), subtask.getEpicId(), - "Subtask не должен быть своим же эпиком"); + // Попытка создания должна вернуть null + Subtask result = taskManager.createSubtask(invalidSubtask); + assertNull(result, "Subtask не должен быть своим же эпиком"); } } \ No newline at end of file From 9a06217cdc9bf586cc3392049306e6147c733b07 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Wed, 8 Oct 2025 16:12:15 +0300 Subject: [PATCH 11/21] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=B4=D0=BB=D0=B8=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/manager/HistoryDataPreservationTest.java | 23 ++----- test/manager/InMemoryTaskManagerTest.java | 67 ++++++------------- .../manager/TaskManagerFunctionalityTest.java | 22 +++--- test/model/TaskIdConflictTest.java | 19 ++---- test/model/TaskTest.java | 12 ++-- 5 files changed, 48 insertions(+), 95 deletions(-) diff --git a/test/manager/HistoryDataPreservationTest.java b/test/manager/HistoryDataPreservationTest.java index b041735..32fab8d 100644 --- a/test/manager/HistoryDataPreservationTest.java +++ b/test/manager/HistoryDataPreservationTest.java @@ -15,39 +15,30 @@ void setUp() { } @Test - void historyPreservesTaskData() { - // Создаем задачу + void historyKeepsData() { Task originalTask = new Task("Original", "Original Description", 1, Status.NEW); - // Добавляем в историю historyManager.add(originalTask); - // Изменяем оригинальную задачу originalTask.setName("Modified"); originalTask.setDescription("Modified Description"); originalTask.setStatus(Status.DONE); - // Получаем задачу из истории Task historicalTask = historyManager.getHistory().get(0); - // Проверяем, что данные в истории сохранились оригинальными - assertEquals("Original", historicalTask.getName(), "История должна сохранить оригинальное имя"); - assertEquals("Original Description", historicalTask.getDescription(), - "История должна сохранить оригинальное описание"); - assertEquals(Status.NEW, historicalTask.getStatus(), "История должна сохранить оригинальный статус"); - assertEquals(1, historicalTask.getId(), "История должна сохранить оригинальный ID"); + assertEquals("Original", historicalTask.getName()); + assertEquals("Original Description", historicalTask.getDescription()); + assertEquals(Status.NEW, historicalTask.getStatus()); + assertEquals(1, historicalTask.getId()); } @Test - void historyContainsIndependentCopies() { + void historyHasCopies() { Task task = new Task("Test", "Description", 1, Status.NEW); historyManager.add(task); - // Изменяем оригинальную задачу task.setName("Changed"); - // Задача в истории не должна измениться - assertNotEquals(task.getName(), historyManager.getHistory().get(0).getName(), - "Задача в истории не должна зависеть от изменений оригинала"); + assertNotEquals(task.getName(), historyManager.getHistory().get(0).getName()); } } \ No newline at end of file diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java index 89b2947..2fe87f7 100644 --- a/test/manager/InMemoryTaskManagerTest.java +++ b/test/manager/InMemoryTaskManagerTest.java @@ -19,7 +19,7 @@ void setUp() { } @Test - void testCreateAndGetTask() { + void testCreateTask() { Task task = new Task("Test Task", "Test Description", Status.NEW); Task createdTask = taskManager.createTask(task); @@ -34,7 +34,7 @@ void testCreateAndGetTask() { } @Test - void testCreateAndGetEpic() { + void testCreateEpic() { Epic epic = new Epic("Test Epic", "Test Epic Description"); Epic createdEpic = taskManager.createEpic(epic); @@ -50,7 +50,7 @@ void testCreateAndGetEpic() { } @Test - void testCreateAndGetSubtask() { + void testCreateSubtask() { Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); Subtask subtask = new Subtask("Test Subtask", "Test Description", Status.NEW, epic.getId()); Subtask createdSubtask = taskManager.createSubtask(subtask); @@ -216,28 +216,23 @@ void testGetAllSubtasks() { } @Test - void testEpicStatusCalculation() { + void testEpicStatus() { Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); - // Пустой эпик - NEW assertEquals(Status.NEW, epic.getStatus()); - // Все подзадачи NEW - эпик NEW Subtask sub1 = taskManager.createSubtask(new Subtask("Sub 1", "Desc", Status.NEW, epic.getId())); Epic updatedEpic1 = taskManager.getEpicById(epic.getId()); assertEquals(Status.NEW, updatedEpic1.getStatus()); - // Все подзадачи DONE - эпик DONE taskManager.updateSubtask(new Subtask("Sub 1", "Desc", sub1.getId(), Status.DONE, epic.getId())); Epic updatedEpic2 = taskManager.getEpicById(epic.getId()); assertEquals(Status.DONE, updatedEpic2.getStatus()); - // Подзадачи NEW и DONE - эпик IN_PROGRESS Subtask sub2 = taskManager.createSubtask(new Subtask("Sub 2", "Desc", Status.NEW, epic.getId())); Epic updatedEpic3 = taskManager.getEpicById(epic.getId()); assertEquals(Status.IN_PROGRESS, updatedEpic3.getStatus()); - // Все подзадачи IN_PROGRESS - эпик IN_PROGRESS taskManager.updateSubtask(new Subtask("Sub 1", "Desc", sub1.getId(), Status.IN_PROGRESS, epic.getId())); taskManager.updateSubtask(new Subtask("Sub 2", "Desc", sub2.getId(), Status.IN_PROGRESS, epic.getId())); Epic updatedEpic4 = taskManager.getEpicById(epic.getId()); @@ -245,15 +240,13 @@ void testEpicStatusCalculation() { } @Test - void testHistoryWithoutDuplicates() { + void testHistoryNoDuplicates() { Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); - // Многократно запрашиваем одну и ту же задачу taskManager.getTaskById(task.getId()); taskManager.getTaskById(task.getId()); taskManager.getTaskById(task.getId()); - // В истории должна быть только одна запись assertEquals(1, taskManager.getHistory().size()); } @@ -262,14 +255,11 @@ void testTaskImmutability() { Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); int originalId = task.getId(); - // Пытаемся изменить задачу через сеттеры task.setId(999); task.setName("Modified"); - // Получаем задачу из менеджера Task managedTask = taskManager.getTaskById(originalId); - // Проверяем, что данные в менеджере не изменились assertEquals(originalId, managedTask.getId()); assertEquals("Test", managedTask.getName()); } @@ -279,144 +269,129 @@ void testEpicImmutability() { Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); int originalId = epic.getId(); - // Пытаемся изменить эпик epic.setId(999); epic.setName("Modified"); - // Получаем эпик из менеджера Epic managedEpic = taskManager.getEpicById(originalId); - // Проверяем, что данные в менеджере не изменились assertEquals(originalId, managedEpic.getId()); assertEquals("Test Epic", managedEpic.getName()); } @Test - void testEmptyHistoryAfterTaskDeletion() { + void testHistoryAfterDelete() { Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); - // Добавляем в историю taskManager.getTaskById(task.getId()); assertEquals(1, taskManager.getHistory().size()); - // Удаляем задачу taskManager.deleteTaskById(task.getId()); - // История должна быть пустой assertTrue(taskManager.getHistory().isEmpty()); } @Test - void testDataIntegrityAfterDeletion() { + void testDataIntegrity() { Epic epic = taskManager.createEpic(new Epic("Test Epic", "Description")); Subtask subtask = taskManager.createSubtask(new Subtask("Subtask", "Desc", Status.NEW, epic.getId())); int subtaskId = subtask.getId(); int epicId = epic.getId(); - // Удаляем подзадачу taskManager.deleteSubtaskById(subtaskId); - // Проверяем, что эпик не содержит удаленную подзадачу Epic updatedEpic = taskManager.getEpicById(epicId); assertFalse(updatedEpic.getSubtaskIds().contains(subtaskId)); - // Проверяем, что подзадача удалена из хранилища assertNull(taskManager.getSubtaskById(subtaskId)); } @Test - void testCreateTaskWithNull() { + void testCreateNullTask() { Task result = taskManager.createTask(null); assertNull(result); } @Test - void testCreateEpicWithNull() { + void testCreateNullEpic() { Epic result = taskManager.createEpic(null); assertNull(result); } @Test - void testCreateSubtaskWithNull() { + void testCreateNullSubtask() { Subtask result = taskManager.createSubtask(null); assertNull(result); } @Test - void testCreateSubtaskWithInvalidEpic() { + void testCreateSubtaskBadEpic() { Subtask subtask = new Subtask("Test", "Desc", Status.NEW, 999); Subtask result = taskManager.createSubtask(subtask); assertNull(result); } @Test - void testUpdateNonExistentTask() { + void testUpdateBadTask() { Task task = new Task("Test", "Desc", 999, Status.NEW); taskManager.updateTask(task); - // Не должно быть исключений assertNull(taskManager.getTaskById(999)); } @Test - void testUpdateNonExistentEpic() { + void testUpdateBadEpic() { Epic epic = new Epic("Test", "Desc"); epic.setId(999); taskManager.updateEpic(epic); - // Не должно быть исключений assertNull(taskManager.getEpicById(999)); } @Test - void testUpdateNonExistentSubtask() { + void testUpdateBadSubtask() { Epic epic = taskManager.createEpic(new Epic("Epic", "Desc")); Subtask subtask = new Subtask("Test", "Desc", 999, Status.NEW, epic.getId()); taskManager.updateSubtask(subtask); - // Не должно быть исключений assertNull(taskManager.getSubtaskById(999)); } @Test - void testDeleteNonExistentTask() { + void testDeleteBadTask() { taskManager.deleteTaskById(999); - // Не должно быть исключений } @Test - void testDeleteNonExistentEpic() { + void testDeleteBadEpic() { taskManager.deleteEpicById(999); - // Не должно быть исключений } @Test - void testDeleteNonExistentSubtask() { + void testDeleteBadSubtask() { taskManager.deleteSubtaskById(999); - // Не должно быть исключений } @Test - void testGetNonExistentTask() { + void testGetBadTask() { Task task = taskManager.getTaskById(999); assertNull(task); } @Test - void testGetNonExistentEpic() { + void testGetBadEpic() { Epic epic = taskManager.getEpicById(999); assertNull(epic); } @Test - void testGetNonExistentSubtask() { + void testGetBadSubtask() { Subtask subtask = taskManager.getSubtaskById(999); assertNull(subtask); } @Test - void testGetSubtasksByNonExistentEpic() { + void testGetSubtasksBadEpic() { List subtasks = taskManager.getSubtasksByEpicId(999); assertTrue(subtasks.isEmpty()); } diff --git a/test/manager/TaskManagerFunctionalityTest.java b/test/manager/TaskManagerFunctionalityTest.java index 36e024b..60e4e49 100644 --- a/test/manager/TaskManagerFunctionalityTest.java +++ b/test/manager/TaskManagerFunctionalityTest.java @@ -18,24 +18,20 @@ void setUp() { @Test void canAddAndFindAllTaskTypes() { - // Создаем задачи всех типов Task task = taskManager.createTask(new Task("Task", "Description", Status.NEW)); Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); Subtask subtask = taskManager.createSubtask(new Subtask("Subtask", "Description", Status.NEW, epic.getId())); - // Проверяем, что все создались - assertNotNull(task, "Задача должна быть создана"); - assertNotNull(epic, "Epic должен быть создан"); - assertNotNull(subtask, "Subtask должен быть создан"); + assertNotNull(task); + assertNotNull(epic); + assertNotNull(subtask); - // Проверяем поиск по ID - assertNotNull(taskManager.getTaskById(task.getId()), "Должна найтись задача по ID"); - assertNotNull(taskManager.getEpicById(epic.getId()), "Должен найтись epic по ID"); - assertNotNull(taskManager.getSubtaskById(subtask.getId()), "Должен найтись subtask по ID"); + assertNotNull(taskManager.getTaskById(task.getId())); + assertNotNull(taskManager.getEpicById(epic.getId())); + assertNotNull(taskManager.getSubtaskById(subtask.getId())); - // Проверяем получение всех задач - assertEquals(1, taskManager.getAllTasks().size(), "Должна быть 1 задача"); - assertEquals(1, taskManager.getAllEpics().size(), "Должен быть 1 epic"); - assertEquals(1, taskManager.getAllSubtasks().size(), "Должен быть 1 subtask"); + assertEquals(1, taskManager.getAllTasks().size()); + assertEquals(1, taskManager.getAllEpics().size()); + assertEquals(1, taskManager.getAllSubtasks().size()); } } \ No newline at end of file diff --git a/test/model/TaskIdConflictTest.java b/test/model/TaskIdConflictTest.java index 571204d..2859284 100644 --- a/test/model/TaskIdConflictTest.java +++ b/test/model/TaskIdConflictTest.java @@ -1,7 +1,5 @@ package model; -import model.Task; -import model.Status; import manager.InMemoryTaskManager; import manager.TaskManager; import org.junit.jupiter.api.BeforeEach; @@ -17,28 +15,23 @@ void setUp() { } @Test - void noConflictBetweenGeneratedAndManualIds() { - // Создаем задачу с автоматически сгенерированным ID + void testNoIdConflict() { Task autoTask = taskManager.createTask(new Task("Auto", "Description", Status.NEW)); - // Создаем задачу вручную с таким же ID Task manualTask = new Task("Manual", "Description", autoTask.getId(), Status.NEW); - // Пытаемся обновить задачу с существующим ID taskManager.updateTask(manualTask); - // Проверяем, что задача обновилась, а не создалась новая - assertEquals(1, taskManager.getAllTasks().size(), "Должна остаться одна задача"); - assertEquals("Manual", taskManager.getTaskById(autoTask.getId()).getName(), - "Задача должна быть обновлена, а не создана новая"); + assertEquals(1, taskManager.getAllTasks().size()); + assertEquals("Manual", taskManager.getTaskById(autoTask.getId()).getName()); } @Test - void tasksWithDifferentIdsCoexist() { + void testDifferentIds() { Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW)); Task task2 = taskManager.createTask(new Task("Task 2", "Description", Status.NEW)); - assertNotEquals(task1.getId(), task2.getId(), "Задачи должны иметь разные ID"); - assertEquals(2, taskManager.getAllTasks().size(), "Обе задачи должны существовать"); + assertNotEquals(task1.getId(), task2.getId()); + assertEquals(2, taskManager.getAllTasks().size()); } } \ No newline at end of file diff --git a/test/model/TaskTest.java b/test/model/TaskTest.java index 67329c9..6f8b618 100644 --- a/test/model/TaskTest.java +++ b/test/model/TaskTest.java @@ -1,26 +1,24 @@ package model; -import model.Status; -import model.Task; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class TaskTest { @Test - void tasksWithSameIdShouldBeEqual() { + void testSameIdEqual() { Task task1 = new Task("Task 1", "Description 1", 1, Status.NEW); Task task2 = new Task("Task 2", "Description 2", 1, Status.DONE); - assertEquals(task1, task2, "Задачи с одинаковым ID должны быть равны"); - assertEquals(task1.hashCode(), task2.hashCode(), "HashCode должен совпадать для одинаковых ID"); + assertEquals(task1, task2); + assertEquals(task1.hashCode(), task2.hashCode()); } @Test - void tasksWithDifferentIdShouldNotBeEqual() { + void testDifferentIdNotEqual() { Task task1 = new Task("Task", "Description", 1, Status.NEW); Task task2 = new Task("Task", "Description", 2, Status.NEW); - assertNotEquals(task1, task2, "Задачи с разными ID не должны быть равны"); + assertNotEquals(task1, task2); } } \ No newline at end of file From f840bb5056eeba1fd975d767f7f29731c7975e79 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Wed, 22 Oct 2025 14:59:30 +0300 Subject: [PATCH 12/21] =?UTF-8?q?feat:=20=D1=80=D0=B5=D0=B0=D0=BB=D0=B8?= =?UTF-8?q?=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20FileBackedTaskManager=20=D1=81?= =?UTF-8?q?=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=D0=BC=20=D0=B2=20=D1=84=D0=B0=D0=B9=D0=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/FileBackedTaskManager.java | 251 +++++++++++++++++++++++++ src/manager/InMemoryTaskManager.java | 13 +- src/manager/ManagerSaveException.java | 11 ++ src/manager/Managers.java | 6 + src/manager/TaskType.java | 7 + test/manager/FileBackedTest.java | 78 ++++++++ 6 files changed, 360 insertions(+), 6 deletions(-) create mode 100644 src/manager/FileBackedTaskManager.java create mode 100644 src/manager/ManagerSaveException.java create mode 100644 src/manager/TaskType.java create mode 100644 test/manager/FileBackedTest.java diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java new file mode 100644 index 0000000..c057220 --- /dev/null +++ b/src/manager/FileBackedTaskManager.java @@ -0,0 +1,251 @@ +package manager; + +import model.*; +import model.TaskType; + +import java.io.*; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; + +public class FileBackedTaskManager extends InMemoryTaskManager { + private final File file; + + public FileBackedTaskManager(File file) { + this.file = file; + } + + @Override + public Task createTask(Task task) { + Task createdTask = super.createTask(task); + save(); + return createdTask; + } + + @Override + public Epic createEpic(Epic epic) { + Epic createdEpic = super.createEpic(epic); + save(); + return createdEpic; + } + + @Override + public Subtask createSubtask(Subtask subtask) { + Subtask createdSubtask = super.createSubtask(subtask); + save(); + return createdSubtask; + } + + @Override + public void updateTask(Task task) { + super.updateTask(task); + save(); + } + + @Override + public void updateEpic(Epic epic) { + super.updateEpic(epic); + save(); + } + + @Override + public void updateSubtask(Subtask subtask) { + super.updateSubtask(subtask); + save(); + } + + @Override + public void deleteTaskById(int id) { + super.deleteTaskById(id); + save(); + } + + @Override + public void deleteEpicById(int id) { + super.deleteEpicById(id); + save(); + } + + @Override + public void deleteSubtaskById(int id) { + super.deleteSubtaskById(id); + save(); + } + + @Override + public void deleteAllTasks() { + super.deleteAllTasks(); + save(); + } + + @Override + public void deleteAllEpics() { + super.deleteAllEpics(); + save(); + } + + @Override + public void deleteAllSubtasks() { + super.deleteAllSubtasks(); + save(); + } + + protected void save() { + try (PrintWriter writer = new PrintWriter(file)) { + // Записываем заголовок + writer.println("id,type,name,status,description,epic"); + + // Сохраняем все задачи + for (Task task : getAllTasks()) { + writer.println(toString(task)); + } + for (Epic epic : getAllEpics()) { + writer.println(toString(epic)); + } + for (Subtask subtask : getAllSubtasks()) { + writer.println(toString(subtask)); + } + } catch (IOException e) { + throw new ManagerSaveException("Ошибка сохранения в файл", e); + } + } + + private String toString(Task task) { + if (task instanceof Epic) { + return String.format("%d,EPIC,%s,%s,%s,", + task.getId(), + task.getName(), + task.getStatus(), + task.getDescription()); + } else if (task instanceof Subtask) { + Subtask subtask = (Subtask) task; + return String.format("%d,SUBTASK,%s,%s,%s,%d", + subtask.getId(), + subtask.getName(), + subtask.getStatus(), + subtask.getDescription(), + subtask.getEpicId()); + } else { + return String.format("%d,TASK,%s,%s,%s,", + task.getId(), + task.getName(), + task.getStatus(), + task.getDescription()); + } + } + + private Task fromString(String value) { + String[] fields = value.split(",", -1); // -1 чтобы сохранить пустые поля + int id = Integer.parseInt(fields[0]); + TaskType type = TaskType.valueOf(fields[1]); + String name = fields[2]; + Status status = Status.valueOf(fields[3]); + String description = fields[4]; + + switch (type) { + case TASK: + Task task = new Task(name, description, status); + task.setId(id); + return task; + case EPIC: + Epic epic = new Epic(name, description); + epic.setId(id); + epic.updateStatus(status); + return epic; + case SUBTASK: + int epicId = fields[5].isEmpty() ? 0 : Integer.parseInt(fields[5]); + Subtask subtask = new Subtask(name, description, status, epicId); + subtask.setId(id); + return subtask; + default: + throw new IllegalArgumentException("Неизвестный тип задачи: " + type); + } + } + + public static FileBackedTaskManager loadFromFile(File file) { + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + try { + if (!file.exists()) { + return manager; + } + + String content = Files.readString(file.toPath()); + String[] lines = content.split("\n"); + + // Пропускаем заголовок и пустые строки + for (int i = 1; i < lines.length; i++) { + String line = lines[i].trim(); + if (line.isEmpty()) { + continue; + } + + Task task = manager.fromString(line); + if (task instanceof Epic) { + manager.epics.put(task.getId(), (Epic) task); + } else if (task instanceof Subtask) { + manager.subtasks.put(task.getId(), (Subtask) task); + } else { + manager.tasks.put(task.getId(), task); + } + + // Обновляем счетчик id + if (task.getId() >= manager.nextId) { + manager.nextId = task.getId() + 1; + } + } + + // Восстанавливаем связи для подзадач в эпиках + for (Subtask subtask : manager.subtasks.values()) { + Epic epic = manager.epics.get(subtask.getEpicId()); + if (epic != null) { + epic.addSubtaskId(subtask.getId()); + } + } + + } catch (IOException e) { + throw new ManagerSaveException("Ошибка загрузки из файла", e); + } + + return manager; + } + + // Дополнительное задание: пользовательский сценарий + public static void main(String[] args) { + // Создаем временный файл для тестирования + try { + File tempFile = File.createTempFile("tasks", ".csv"); + + // Создаем первый менеджер и добавляем задачи + FileBackedTaskManager manager1 = new FileBackedTaskManager(tempFile); + + Task task1 = manager1.createTask(new Task("Task 1", "Description 1", Status.NEW)); + manager1.createTask(new Task("Task 2", "Description 2", Status.IN_PROGRESS)); + + Epic epic1 = manager1.createEpic(new Epic("Epic 1", "Epic description")); + Subtask subtask1 = manager1.createSubtask(new Subtask("Subtask 1", "Sub description", Status.DONE, epic1.getId())); + manager1.createSubtask(new Subtask("Subtask 2", "Sub description 2", Status.NEW, epic1.getId())); + + // Просматриваем некоторые задачи для истории + manager1.getTaskById(task1.getId()); + manager1.getEpicById(epic1.getId()); + manager1.getSubtaskById(subtask1.getId()); + + // Создаем второй менеджер из того же файла + FileBackedTaskManager manager2 = FileBackedTaskManager.loadFromFile(tempFile); + + // Проверяем, что все задачи восстановились + System.out.println("Проверка восстановления данных:"); + System.out.println("Задачи в manager2: " + manager2.getAllTasks().size()); + System.out.println("Эпики в manager2: " + manager2.getAllEpics().size()); + System.out.println("Подзадачи в manager2: " + manager2.getAllSubtasks().size()); + System.out.println("История в manager2: " + manager2.getHistory().size()); + + // Очищаем временный файл + tempFile.deleteOnExit(); + + } catch (IOException e) { + System.err.println("Ошибка при работе с файлом: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 7ce0480..12ddde1 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -6,20 +6,21 @@ import java.util.*; public class InMemoryTaskManager implements TaskManager { - private final Map tasks = new HashMap<>(); - private final Map epics = new HashMap<>(); - private final Map subtasks = new HashMap<>(); - private final HistoryManager historyManager; - private int nextId = 1; + protected final Map tasks = new HashMap<>(); + protected final Map epics = new HashMap<>(); + protected final Map subtasks = new HashMap<>(); + protected final HistoryManager historyManager; + protected int nextId = 1; public InMemoryTaskManager() { this.historyManager = Managers.getDefaultHistory(); } - private int generateId() { + protected int generateId() { return nextId++; } + // Остальные методы остаются без изменений... private Task createTaskCopy(Task original) { return new Task(original.getName(), original.getDescription(), original.getId(), original.getStatus()); diff --git a/src/manager/ManagerSaveException.java b/src/manager/ManagerSaveException.java new file mode 100644 index 0000000..4ff500a --- /dev/null +++ b/src/manager/ManagerSaveException.java @@ -0,0 +1,11 @@ +package manager; + +public class ManagerSaveException extends RuntimeException { + public ManagerSaveException(String message) { + super(message); + } + + public ManagerSaveException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/src/manager/Managers.java b/src/manager/Managers.java index 64dfb6f..e5d3bce 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 getDefault() { @@ -9,4 +11,8 @@ public static TaskManager getDefault() { public static HistoryManager getDefaultHistory() { return new InMemoryHistoryManager(); } + + public static FileBackedTaskManager getFileBackedTaskManager(File file) { + return new FileBackedTaskManager(file); + } } \ No newline at end of file diff --git a/src/manager/TaskType.java b/src/manager/TaskType.java new file mode 100644 index 0000000..b95e27a --- /dev/null +++ b/src/manager/TaskType.java @@ -0,0 +1,7 @@ +package model; + +public enum TaskType { + TASK, + EPIC, + SUBTASK +} \ No newline at end of file diff --git a/test/manager/FileBackedTest.java b/test/manager/FileBackedTest.java new file mode 100644 index 0000000..ce61ca6 --- /dev/null +++ b/test/manager/FileBackedTest.java @@ -0,0 +1,78 @@ +package manager; + +import manager.FileBackedTaskManager; +import manager.ManagerSaveException; +import model.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +class FileBackedTaskManagerTest { + + @TempDir + Path tempDir; + + @Test + void testSaveAndLoadEmptyFile() { + File file = tempDir.resolve("test.csv").toFile(); + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + // Сохраняем пустой менеджер + manager.save(); + + // Загружаем из файла + FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(file); + + assertTrue(loadedManager.getAllTasks().isEmpty()); + assertTrue(loadedManager.getAllEpics().isEmpty()); + assertTrue(loadedManager.getAllSubtasks().isEmpty()); + } + + @Test + void testSaveAndLoadMultipleTasks() { + File file = tempDir.resolve("test.csv").toFile(); + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + // Создаем задачи + Task task1 = manager.createTask(new Task("Task 1", "Description 1", Status.NEW)); + manager.createTask(new Task("Task 2", "Description 2", Status.DONE)); + + Epic epic = manager.createEpic(new Epic("Epic", "Epic description")); + manager.createSubtask(new Subtask("Subtask", "Sub description", Status.IN_PROGRESS, epic.getId())); + + // Загружаем из файла + FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(file); + + assertEquals(2, loadedManager.getAllTasks().size()); + assertEquals(1, loadedManager.getAllEpics().size()); + assertEquals(1, loadedManager.getAllSubtasks().size()); + + Task loadedTask = loadedManager.getTaskById(task1.getId()); + assertNotNull(loadedTask); + assertEquals("Task 1", loadedTask.getName()); + assertEquals(Status.NEW, loadedTask.getStatus()); + } + + @Test + void testSaveException() { + File file = new File("/invalid/path/tasks.csv"); + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + assertThrows(ManagerSaveException.class, () -> { + manager.createTask(new Task("Test", "Test", Status.NEW)); + }); + } + + @Test + void testLoadFromNonExistentFile() { + File file = new File("nonexistent.csv"); + FileBackedTaskManager manager = FileBackedTaskManager.loadFromFile(file); + + assertNotNull(manager); + assertTrue(manager.getAllTasks().isEmpty()); + } +} \ No newline at end of file From 5df0d2e8aad1864ad9f3a2244b55df7c4ddc1d32 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Wed, 22 Oct 2025 15:10:29 +0300 Subject: [PATCH 13/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20FileBackedTaskManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/FileBackedTaskManager.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index c057220..26f1530 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -5,8 +5,6 @@ import java.io.*; import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; public class FileBackedTaskManager extends InMemoryTaskManager { private final File file; From a8eafb94f9679bfa5fdf9b4b6b58cb3f7a705ae2 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Wed, 22 Oct 2025 20:48:57 +0300 Subject: [PATCH 14/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F:=20=D0=B1=D0=BE=D0=BB=D0=B5?= =?UTF-8?q?=D0=B5=20=D0=BE=D1=81=D0=BC=D1=8B=D1=81=D0=BB=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B8=20String.format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/FileBackedTaskManager.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index 26f1530..2c9a07a 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -95,20 +95,20 @@ protected void save() { // Сохраняем все задачи for (Task task : getAllTasks()) { - writer.println(toString(task)); + writer.println(taskToString(task)); } for (Epic epic : getAllEpics()) { - writer.println(toString(epic)); + writer.println(taskToString(epic)); } for (Subtask subtask : getAllSubtasks()) { - writer.println(toString(subtask)); + writer.println(taskToString(subtask)); } } catch (IOException e) { throw new ManagerSaveException("Ошибка сохранения в файл", e); } } - private String toString(Task task) { + private String taskToString(Task task) { if (task instanceof Epic) { return String.format("%d,EPIC,%s,%s,%s,", task.getId(), @@ -132,7 +132,7 @@ private String toString(Task task) { } } - private Task fromString(String value) { + private Task taskFromString(String value) { String[] fields = value.split(",", -1); // -1 чтобы сохранить пустые поля int id = Integer.parseInt(fields[0]); TaskType type = TaskType.valueOf(fields[1]); @@ -156,7 +156,7 @@ private Task fromString(String value) { subtask.setId(id); return subtask; default: - throw new IllegalArgumentException("Неизвестный тип задачи: " + type); + throw new IllegalArgumentException(String.format("Неизвестный тип задачи: %s", type)); } } @@ -169,16 +169,16 @@ public static FileBackedTaskManager loadFromFile(File file) { } String content = Files.readString(file.toPath()); - String[] lines = content.split("\n"); + String[] fileLines = content.split("\n"); // Пропускаем заголовок и пустые строки - for (int i = 1; i < lines.length; i++) { - String line = lines[i].trim(); - if (line.isEmpty()) { + for (int lineNumber = 1; lineNumber < fileLines.length; lineNumber++) { + String currentLine = fileLines[lineNumber].trim(); + if (currentLine.isEmpty()) { continue; } - Task task = manager.fromString(line); + Task task = manager.taskFromString(currentLine); if (task instanceof Epic) { manager.epics.put(task.getId(), (Epic) task); } else if (task instanceof Subtask) { From d9a3705ea3909b4355c388aa33d6d83abe8ab328 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Thu, 6 Nov 2025 15:37:52 +0300 Subject: [PATCH 15/21] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D1=82=D1=81=D0=BB=D0=B5?= =?UTF-8?q?=D0=B6=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F=20=D0=B2=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D0=B8,=20=D0=BF=D1=80=D0=B8=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D1=82=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8=20=D0=B8=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D1=81=D0=B5=D1=87=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлены поля duration, startTime в Task, Epic, Subtask - Реализован метод getEndTime() - Добавлен getPrioritizedTasks() с TreeSet для доступа за O(n) - Реализованы методы проверки пересечений по времени - Добавлены Optional методы для поиска задач - Обновлена сериализация/десериализация в FileBackedTaskManager - Добавлено полное тестовое покрытие новой функциональности - Использован Stream API в нескольких методах - Реализована проверка статуса Epic для граничных условий - Добавлены тесты для HistoryManager --- src/manager/FileBackedTaskManager.java | 85 ++++-- src/manager/InMemoryTaskManager.java | 310 +++++++++++++++----- src/manager/TaskManager.java | 25 +- src/model/Epic.java | 25 +- src/model/Subtask.java | 22 +- src/model/Task.java | 58 +++- test/TaskManagerTest.java | 313 +++++++++++++++++++++ test/manager/ExceptionHandlingTest.java | 35 +++ test/manager/FileBackedTest.java | 81 ++++++ test/manager/InMemoryTaskManagerTest.java | 63 +++++ test/model/InMemoryHistoryManagerTest.java | 61 ++++ test/model/TaskTest.java | 24 -- 12 files changed, 950 insertions(+), 152 deletions(-) create mode 100644 test/TaskManagerTest.java create mode 100644 test/manager/ExceptionHandlingTest.java delete mode 100644 test/model/TaskTest.java diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index 2c9a07a..a1e9a7b 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -5,9 +5,13 @@ import java.io.*; import java.nio.file.Files; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; public class FileBackedTaskManager extends InMemoryTaskManager { private final File file; + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME; public FileBackedTaskManager(File file) { this.file = file; @@ -90,10 +94,8 @@ public void deleteAllSubtasks() { protected void save() { try (PrintWriter writer = new PrintWriter(file)) { - // Записываем заголовок - writer.println("id,type,name,status,description,epic"); + writer.println("id,type,name,status,description,epic,duration,startTime"); - // Сохраняем все задачи for (Task task : getAllTasks()) { writer.println(taskToString(task)); } @@ -109,51 +111,71 @@ protected void save() { } private String taskToString(Task task) { + String durationStr = task.getDuration() != null ? + String.valueOf(task.getDuration().toMinutes()) : ""; + String startTimeStr = task.getStartTime() != null ? + task.getStartTime().format(DATE_TIME_FORMATTER) : ""; + if (task instanceof Epic) { - return String.format("%d,EPIC,%s,%s,%s,", + Epic epic = (Epic) task; + String endTimeStr = epic.getEndTime() != null ? + epic.getEndTime().format(DATE_TIME_FORMATTER) : ""; + return String.format("%d,EPIC,%s,%s,%s,,%s,%s,%s", task.getId(), task.getName(), task.getStatus(), - task.getDescription()); + task.getDescription(), + durationStr, + startTimeStr, + endTimeStr); } else if (task instanceof Subtask) { Subtask subtask = (Subtask) task; - return String.format("%d,SUBTASK,%s,%s,%s,%d", + return String.format("%d,SUBTASK,%s,%s,%s,%d,%s,%s", subtask.getId(), subtask.getName(), subtask.getStatus(), subtask.getDescription(), - subtask.getEpicId()); + subtask.getEpicId(), + durationStr, + startTimeStr); } else { - return String.format("%d,TASK,%s,%s,%s,", + return String.format("%d,TASK,%s,%s,%s,,%s,%s", task.getId(), task.getName(), task.getStatus(), - task.getDescription()); + task.getDescription(), + durationStr, + startTimeStr); } } private Task taskFromString(String value) { - String[] fields = value.split(",", -1); // -1 чтобы сохранить пустые поля + String[] fields = value.split(",", -1); int id = Integer.parseInt(fields[0]); TaskType type = TaskType.valueOf(fields[1]); String name = fields[2]; Status status = Status.valueOf(fields[3]); String description = fields[4]; + Duration duration = fields[6].isEmpty() ? Duration.ZERO : + Duration.ofMinutes(Long.parseLong(fields[6])); + LocalDateTime startTime = fields[7].isEmpty() ? null : + LocalDateTime.parse(fields[7], DATE_TIME_FORMATTER); + switch (type) { case TASK: - Task task = new Task(name, description, status); - task.setId(id); + Task task = new Task(name, description, id, status, duration, startTime); return task; case EPIC: - Epic epic = new Epic(name, description); - epic.setId(id); - epic.updateStatus(status); + Epic epic = new Epic(name, description, id, status, duration, startTime, null); + if (fields.length > 8 && !fields[8].isEmpty()) { + LocalDateTime endTime = LocalDateTime.parse(fields[8], DATE_TIME_FORMATTER); + epic.setEndTime(endTime); + } return epic; case SUBTASK: int epicId = fields[5].isEmpty() ? 0 : Integer.parseInt(fields[5]); - Subtask subtask = new Subtask(name, description, status, epicId); - subtask.setId(id); + Subtask subtask = new Subtask(name, description, id, status, epicId, duration, startTime); return subtask; default: throw new IllegalArgumentException(String.format("Неизвестный тип задачи: %s", type)); @@ -171,7 +193,6 @@ public static FileBackedTaskManager loadFromFile(File file) { String content = Files.readString(file.toPath()); String[] fileLines = content.split("\n"); - // Пропускаем заголовок и пустые строки for (int lineNumber = 1; lineNumber < fileLines.length; lineNumber++) { String currentLine = fileLines[lineNumber].trim(); if (currentLine.isEmpty()) { @@ -187,13 +208,15 @@ public static FileBackedTaskManager loadFromFile(File file) { manager.tasks.put(task.getId(), task); } - // Обновляем счетчик id if (task.getId() >= manager.nextId) { manager.nextId = task.getId() + 1; } + + if (task.getStartTime() != null) { + manager.prioritizedTasks.add(task); + } } - // Восстанавливаем связи для подзадач в эпиках for (Subtask subtask : manager.subtasks.values()) { Epic epic = manager.epics.get(subtask.getEpicId()); if (epic != null) { @@ -208,38 +231,38 @@ public static FileBackedTaskManager loadFromFile(File file) { return manager; } - // Дополнительное задание: пользовательский сценарий public static void main(String[] args) { - // Создаем временный файл для тестирования try { File tempFile = File.createTempFile("tasks", ".csv"); - // Создаем первый менеджер и добавляем задачи FileBackedTaskManager manager1 = new FileBackedTaskManager(tempFile); - Task task1 = manager1.createTask(new Task("Task 1", "Description 1", Status.NEW)); - manager1.createTask(new Task("Task 2", "Description 2", Status.IN_PROGRESS)); + Task task1 = manager1.createTask(new Task("Task 1", "Description 1", Status.NEW, + Duration.ofMinutes(30), LocalDateTime.now())); + manager1.createTask(new Task("Task 2", "Description 2", Status.IN_PROGRESS, + Duration.ofHours(1), LocalDateTime.now().plusHours(2))); Epic epic1 = manager1.createEpic(new Epic("Epic 1", "Epic description")); - Subtask subtask1 = manager1.createSubtask(new Subtask("Subtask 1", "Sub description", Status.DONE, epic1.getId())); - manager1.createSubtask(new Subtask("Subtask 2", "Sub description 2", Status.NEW, epic1.getId())); + Subtask subtask1 = manager1.createSubtask(new Subtask("Subtask 1", "Sub description", + Status.DONE, epic1.getId(), Duration.ofMinutes(45), + LocalDateTime.now().plusHours(1))); + manager1.createSubtask(new Subtask("Subtask 2", "Sub description 2", + Status.NEW, epic1.getId(), Duration.ofMinutes(15), + LocalDateTime.now().plusHours(3))); - // Просматриваем некоторые задачи для истории manager1.getTaskById(task1.getId()); manager1.getEpicById(epic1.getId()); manager1.getSubtaskById(subtask1.getId()); - // Создаем второй менеджер из того же файла FileBackedTaskManager manager2 = FileBackedTaskManager.loadFromFile(tempFile); - // Проверяем, что все задачи восстановились System.out.println("Проверка восстановления данных:"); System.out.println("Задачи в manager2: " + manager2.getAllTasks().size()); System.out.println("Эпики в manager2: " + manager2.getAllEpics().size()); System.out.println("Подзадачи в manager2: " + manager2.getAllSubtasks().size()); System.out.println("История в manager2: " + manager2.getHistory().size()); + System.out.println("Приоритетные задачи в manager2: " + manager2.getPrioritizedTasks().size()); - // Очищаем временный файл tempFile.deleteOnExit(); } catch (IOException e) { diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index 12ddde1..f48a621 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -3,7 +3,13 @@ import model.Task; import model.Epic; import model.Subtask; +import model.Status; + +import java.time.Duration; +import java.time.LocalDateTime; import java.util.*; +import java.util.stream.Collectors; +import java.util.Optional; public class InMemoryTaskManager implements TaskManager { protected final Map tasks = new HashMap<>(); @@ -11,6 +17,11 @@ public class InMemoryTaskManager implements TaskManager { protected final Map subtasks = new HashMap<>(); protected final HistoryManager historyManager; protected int nextId = 1; + protected final Set prioritizedTasks = new TreeSet<>( + Comparator.comparing(Task::getStartTime, + Comparator.nullsLast(Comparator.naturalOrder())) + .thenComparing(Task::getId) + ); public InMemoryTaskManager() { this.historyManager = Managers.getDefaultHistory(); @@ -20,16 +31,105 @@ protected int generateId() { return nextId++; } - // Остальные методы остаются без изменений... + @Override + public Optional getTaskByIdOptional(int id) { + Task task = tasks.get(id); + if (task != null) { + historyManager.add(task); + return Optional.of(createTaskCopy(task)); + } + return Optional.empty(); + } + + @Override + public Optional getEpicByIdOptional(int id) { + Epic epic = epics.get(id); + if (epic != null) { + historyManager.add(epic); + return Optional.of(createEpicCopy(epic)); + } + return Optional.empty(); + } + + @Override + public Optional getSubtaskByIdOptional(int id) { + Subtask subtask = subtasks.get(id); + if (subtask != null) { + historyManager.add(subtask); + return Optional.of(createSubtaskCopy(subtask)); + } + return Optional.empty(); + } + + @Override + public boolean isTasksOverlapping(Task task1, Task task2) { + if (task1.getStartTime() == null || task2.getStartTime() == null || + task1.getEndTime() == null || task2.getEndTime() == null) { + return false; + } + + return !task1.getEndTime().isBefore(task2.getStartTime()) && + !task1.getStartTime().isAfter(task2.getEndTime()); + } + + @Override + public boolean hasAnyTimeOverlaps() { + List prioritized = getPrioritizedTasks(); + + for (int i = 1; i < prioritized.size(); i++) { + Task current = prioritized.get(i); + Task previous = prioritized.get(i - 1); + + if (isTasksOverlapping(previous, current)) { + return true; + } + } + return false; + } + + @Override + public boolean isTaskOverlapping(Task task) { + if (task.getStartTime() == null) { + return false; + } + + LocalDateTime taskStart = task.getStartTime(); + LocalDateTime taskEnd = task.getEndTime(); + + if (taskEnd == null) { + return false; + } + + return prioritizedTasks.stream() + .filter(t -> t.getStartTime() != null && t.getEndTime() != null) + .filter(t -> t.getId() != task.getId()) + .anyMatch(existingTask -> isTasksOverlapping(task, existingTask)); + } + + @Override + public List getSubtasksByEpicId(int epicId) { + Epic epic = epics.get(epicId); + if (epic == null) { + return Collections.emptyList(); + } + + return epic.getSubtaskIds().stream() + .map(subtasks::get) + .filter(Objects::nonNull) + .map(this::createSubtaskCopy) + .collect(Collectors.toList()); + } + private Task createTaskCopy(Task original) { return new Task(original.getName(), original.getDescription(), - original.getId(), original.getStatus()); + original.getId(), original.getStatus(), + original.getDuration(), original.getStartTime()); } private Epic createEpicCopy(Epic original) { - Epic copy = new Epic(original.getName(), original.getDescription()); - copy.setId(original.getId()); - copy.updateStatus(original.getStatus()); + Epic copy = new Epic(original.getName(), original.getDescription(), + original.getId(), original.getStatus(), + original.getDuration(), original.getStartTime(), original.getEndTime()); for (Integer subtaskId : original.getSubtaskIds()) { copy.addSubtaskId(subtaskId); } @@ -38,34 +138,29 @@ private Epic createEpicCopy(Epic original) { private Subtask createSubtaskCopy(Subtask original) { return new Subtask(original.getName(), original.getDescription(), - original.getId(), original.getStatus(), original.getEpicId()); + original.getId(), original.getStatus(), original.getEpicId(), + original.getDuration(), original.getStartTime()); } @Override public List getAllTasks() { - List taskCopies = new ArrayList<>(); - for (Task task : tasks.values()) { - taskCopies.add(createTaskCopy(task)); - } - return taskCopies; + return tasks.values().stream() + .map(this::createTaskCopy) + .collect(Collectors.toList()); } @Override public void deleteAllTasks() { for (Integer taskId : tasks.keySet()) { historyManager.remove(taskId); + prioritizedTasks.removeIf(task -> task.getId() == taskId); } tasks.clear(); } @Override public Task getTaskById(int id) { - Task task = tasks.get(id); - if (task != null) { - historyManager.add(task); - return createTaskCopy(task); - } - return null; + return getTaskByIdOptional(id).orElse(null); } @Override @@ -73,8 +168,20 @@ public Task createTask(Task task) { if (task == null) { return null; } - Task newTask = new Task(task.getName(), task.getDescription(), generateId(), task.getStatus()); + + if (isTaskOverlapping(task)) { + return null; + } + + Task newTask = new Task(task.getName(), task.getDescription(), + generateId(), task.getStatus(), + task.getDuration(), task.getStartTime()); tasks.put(newTask.getId(), newTask); + + if (newTask.getStartTime() != null) { + prioritizedTasks.add(newTask); + } + return createTaskCopy(newTask); } @@ -83,22 +190,36 @@ public void updateTask(Task task) { if (task == null || !tasks.containsKey(task.getId())) { return; } + + Task existingTask = tasks.get(task.getId()); + prioritizedTasks.remove(existingTask); + + if (isTaskOverlapping(task)) { + prioritizedTasks.add(existingTask); + return; + } + tasks.put(task.getId(), task); + + if (task.getStartTime() != null) { + prioritizedTasks.add(task); + } } @Override public void deleteTaskById(int id) { - tasks.remove(id); - historyManager.remove(id); + Task task = tasks.remove(id); + if (task != null) { + historyManager.remove(id); + prioritizedTasks.remove(task); + } } @Override public List getAllEpics() { - List epicCopies = new ArrayList<>(); - for (Epic epic : epics.values()) { - epicCopies.add(createEpicCopy(epic)); - } - return epicCopies; + return epics.values().stream() + .map(this::createEpicCopy) + .collect(Collectors.toList()); } @Override @@ -108,6 +229,7 @@ public void deleteAllEpics() { } for (Integer subtaskId : subtasks.keySet()) { historyManager.remove(subtaskId); + prioritizedTasks.removeIf(task -> task.getId() == subtaskId); } for (Epic epic : epics.values()) { @@ -116,16 +238,12 @@ public void deleteAllEpics() { } } epics.clear(); + subtasks.clear(); } @Override public Epic getEpicById(int id) { - Epic epic = epics.get(id); - if (epic != null) { - historyManager.add(epic); - return createEpicCopy(epic); - } - return null; + return getEpicByIdOptional(id).orElse(null); } @Override @@ -157,6 +275,7 @@ public void deleteEpicById(int id) { for (Integer subtaskId : epic.getSubtaskIds()) { subtasks.remove(subtaskId); historyManager.remove(subtaskId); + prioritizedTasks.removeIf(task -> task.getId() == subtaskId); } epics.remove(id); } @@ -164,34 +283,29 @@ public void deleteEpicById(int id) { @Override public List getAllSubtasks() { - List subtaskCopies = new ArrayList<>(); - for (Subtask subtask : subtasks.values()) { - subtaskCopies.add(createSubtaskCopy(subtask)); - } - return subtaskCopies; + return subtasks.values().stream() + .map(this::createSubtaskCopy) + .collect(Collectors.toList()); } @Override public void deleteAllSubtasks() { for (Integer subtaskId : subtasks.keySet()) { historyManager.remove(subtaskId); + prioritizedTasks.removeIf(task -> task.getId() == subtaskId); } for (Epic epic : epics.values()) { epic.clearSubtasks(); updateEpicStatus(epic.getId()); + updateEpicTiming(epic.getId()); } subtasks.clear(); } @Override public Subtask getSubtaskById(int id) { - Subtask subtask = subtasks.get(id); - if (subtask != null) { - historyManager.add(subtask); - return createSubtaskCopy(subtask); - } - return null; + return getSubtaskByIdOptional(id).orElse(null); } @Override @@ -202,23 +316,31 @@ public Subtask createSubtask(Subtask subtask) { Epic epic = epics.get(subtask.getEpicId()); - // Усиленная проверка циклических ссылок if (subtask.getId() != 0) { - // Подзадача не может быть своим же эпиком if (subtask.getEpicId() == subtask.getId()) { return null; } - // Эпик не может быть своей же подзадачей if (epic.getId() == subtask.getId()) { return null; } } + if (isTaskOverlapping(subtask)) { + return null; + } + Subtask newSubtask = new Subtask(subtask.getName(), subtask.getDescription(), - generateId(), subtask.getStatus(), subtask.getEpicId()); + generateId(), subtask.getStatus(), subtask.getEpicId(), + subtask.getDuration(), subtask.getStartTime()); subtasks.put(newSubtask.getId(), newSubtask); epic.addSubtaskId(newSubtask.getId()); + + if (newSubtask.getStartTime() != null) { + prioritizedTasks.add(newSubtask); + } + updateEpicStatus(epic.getId()); + updateEpicTiming(epic.getId()); return createSubtaskCopy(newSubtask); } @@ -228,17 +350,22 @@ public void updateSubtask(Subtask subtask) { return; } - // Проверка: подзадача не может быть своим же эпиком if (subtask.getEpicId() == subtask.getId()) { return; } - // Проверка: эпик должен существовать if (!epics.containsKey(subtask.getEpicId())) { return; } Subtask existingSubtask = subtasks.get(subtask.getId()); + prioritizedTasks.remove(existingSubtask); + + if (isTaskOverlapping(subtask)) { + prioritizedTasks.add(existingSubtask); + return; + } + int oldEpicId = existingSubtask.getEpicId(); int newEpicId = subtask.getEpicId(); @@ -247,6 +374,7 @@ public void updateSubtask(Subtask subtask) { if (oldEpic != null) { oldEpic.removeSubtaskId(subtask.getId()); updateEpicStatus(oldEpicId); + updateEpicTiming(oldEpicId); } Epic newEpic = epics.get(newEpicId); if (newEpic != null) { @@ -254,14 +382,19 @@ public void updateSubtask(Subtask subtask) { } } - // Обновляем существующую подзадачу, а не заменяем ее existingSubtask.setName(subtask.getName()); existingSubtask.setDescription(subtask.getDescription()); existingSubtask.setStatus(subtask.getStatus()); existingSubtask.setEpicId(subtask.getEpicId()); + existingSubtask.setDuration(subtask.getDuration()); + existingSubtask.setStartTime(subtask.getStartTime()); + + if (existingSubtask.getStartTime() != null) { + prioritizedTasks.add(existingSubtask); + } - // Всегда обновляем статус эпика updateEpicStatus(subtask.getEpicId()); + updateEpicTiming(subtask.getEpicId()); } @Override @@ -272,30 +405,22 @@ public void deleteSubtaskById(int id) { if (epic != null) { epic.removeSubtaskId(id); updateEpicStatus(epic.getId()); + updateEpicTiming(epic.getId()); } subtasks.remove(id); historyManager.remove(id); + prioritizedTasks.remove(subtask); } } @Override - public List getSubtasksByEpicId(int epicId) { - List result = new ArrayList<>(); - Epic epic = epics.get(epicId); - if (epic != null) { - for (Integer subtaskId : epic.getSubtaskIds()) { - Subtask subtask = subtasks.get(subtaskId); - if (subtask != null) { - result.add(createSubtaskCopy(subtask)); - } - } - } - return result; + public List getHistory() { + return historyManager.getHistory(); } @Override - public List getHistory() { - return historyManager.getHistory(); + public List getPrioritizedTasks() { + return new ArrayList<>(prioritizedTasks); } private void updateEpicStatus(int epicId) { @@ -305,32 +430,71 @@ private void updateEpicStatus(int epicId) { } List subtaskIds = epic.getSubtaskIds(); if (subtaskIds.isEmpty()) { - epic.updateStatus(model.Status.NEW); + epic.updateStatus(Status.NEW); return; } + boolean allDone = true; boolean allNew = true; + for (Integer subtaskId : subtaskIds) { Subtask subtask = subtasks.get(subtaskId); if (subtask == null) { continue; } - if (subtask.getStatus() != model.Status.DONE) { + if (subtask.getStatus() != Status.DONE) { allDone = false; } - if (subtask.getStatus() != model.Status.NEW) { + if (subtask.getStatus() != Status.NEW) { allNew = false; } - if (!allDone && !allNew) { - break; - } } + if (allDone) { - epic.updateStatus(model.Status.DONE); + epic.updateStatus(Status.DONE); } else if (allNew) { - epic.updateStatus(model.Status.NEW); + epic.updateStatus(Status.NEW); } else { - epic.updateStatus(model.Status.IN_PROGRESS); + epic.updateStatus(Status.IN_PROGRESS); } } + + private void updateEpicTiming(int epicId) { + Epic epic = epics.get(epicId); + if (epic == null) { + return; + } + + List epicSubtasks = getSubtasksByEpicId(epicId).stream() + .map(subtask -> subtasks.get(subtask.getId())) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + if (epicSubtasks.isEmpty()) { + epic.setDuration(Duration.ZERO); + epic.setStartTime(null); + epic.setEndTime(null); + return; + } + + Duration totalDuration = epicSubtasks.stream() + .map(Subtask::getDuration) + .filter(Objects::nonNull) + .reduce(Duration.ZERO, Duration::plus); + epic.setDuration(totalDuration); + + LocalDateTime earliestStart = epicSubtasks.stream() + .map(Subtask::getStartTime) + .filter(Objects::nonNull) + .min(LocalDateTime::compareTo) + .orElse(null); + epic.setStartTime(earliestStart); + + LocalDateTime latestEnd = epicSubtasks.stream() + .map(Subtask::getEndTime) + .filter(Objects::nonNull) + .max(LocalDateTime::compareTo) + .orElse(null); + epic.setEndTime(latestEnd); + } } \ No newline at end of file diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java index 39943b0..e9f0f43 100644 --- a/src/manager/TaskManager.java +++ b/src/manager/TaskManager.java @@ -4,45 +4,38 @@ import model.Epic; import model.Subtask; import java.util.List; +import java.util.Optional; public interface TaskManager { List getAllTasks(); - void deleteAllTasks(); - Task getTaskById(int id); - + Optional getTaskByIdOptional(int id); Task createTask(Task task); - void updateTask(Task task); - void deleteTaskById(int id); List getAllEpics(); - void deleteAllEpics(); - Epic getEpicById(int id); - + Optional getEpicByIdOptional(int id); Epic createEpic(Epic epic); - void updateEpic(Epic epic); - void deleteEpicById(int id); List getAllSubtasks(); - void deleteAllSubtasks(); - Subtask getSubtaskById(int id); - + Optional getSubtaskByIdOptional(int id); Subtask createSubtask(Subtask subtask); - void updateSubtask(Subtask subtask); - void deleteSubtaskById(int id); List getSubtasksByEpicId(int epicId); - List getHistory(); + + List getPrioritizedTasks(); + boolean isTaskOverlapping(Task task); + boolean isTasksOverlapping(Task task1, Task task2); + boolean hasAnyTimeOverlaps(); } \ No newline at end of file diff --git a/src/model/Epic.java b/src/model/Epic.java index c4569f8..726cb44 100644 --- a/src/model/Epic.java +++ b/src/model/Epic.java @@ -1,14 +1,25 @@ package model; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; public class Epic extends Task { private List subtaskIds; + private LocalDateTime endTime; public Epic(String name, String description) { super(name, description, Status.NEW); this.subtaskIds = new ArrayList<>(); + this.duration = Duration.ZERO; + } + + public Epic(String name, String description, int id, Status status, Duration duration, + LocalDateTime startTime, LocalDateTime endTime) { + super(name, description, id, status, duration, startTime); + this.subtaskIds = new ArrayList<>(); + this.endTime = endTime; } public List getSubtaskIds() { @@ -38,9 +49,19 @@ public void updateStatus(Status status) { this.status = status; } + @Override + public LocalDateTime getEndTime() { + return endTime; + } + + public void setEndTime(LocalDateTime endTime) { + this.endTime = endTime; + } + @Override public String toString() { - return String.format("Epic{name='%s', description='%s', id=%d, status=%s, subtaskIds=%s}", - name, description, id, status, subtaskIds); + return String.format("Epic{name='%s', description='%s', id=%d, status=%s, " + + "duration=%s, startTime=%s, endTime=%s, subtaskIds=%s}", + name, description, id, status, duration, startTime, endTime, subtaskIds); } } \ No newline at end of file diff --git a/src/model/Subtask.java b/src/model/Subtask.java index 6f089fc..dfe9b32 100644 --- a/src/model/Subtask.java +++ b/src/model/Subtask.java @@ -1,16 +1,33 @@ package model; +import java.time.Duration; +import java.time.LocalDateTime; + public class Subtask extends Task { private int epicId; public Subtask(String name, String description, Status status, int epicId) { super(name, description, status); this.epicId = epicId; + this.duration = Duration.ZERO; } public Subtask(String name, String description, int id, Status status, int epicId) { super(name, description, id, status); this.epicId = epicId; + this.duration = Duration.ZERO; + } + + public Subtask(String name, String description, Status status, int epicId, + Duration duration, LocalDateTime startTime) { + super(name, description, status, duration, startTime); + this.epicId = epicId; + } + + public Subtask(String name, String description, int id, Status status, int epicId, + Duration duration, LocalDateTime startTime) { + super(name, description, id, status, duration, startTime); + this.epicId = epicId; } public int getEpicId() { @@ -23,7 +40,8 @@ public void setEpicId(int epicId) { @Override public String toString() { - return String.format("Subtask{name='%s', description='%s', id=%d, status=%s, epicId=%d}", - name, description, id, status, epicId); + return String.format("Subtask{name='%s', description='%s', id=%d, status=%s, " + + "duration=%s, startTime=%s, epicId=%d}", + name, description, id, status, duration, startTime, epicId); } } \ No newline at end of file diff --git a/src/model/Task.java b/src/model/Task.java index 9754510..35689d8 100644 --- a/src/model/Task.java +++ b/src/model/Task.java @@ -1,5 +1,7 @@ package model; +import java.time.Duration; +import java.time.LocalDateTime; import java.util.Objects; public class Task { @@ -7,11 +9,14 @@ public class Task { protected String description; protected int id; protected Status status; + protected Duration duration; + protected LocalDateTime startTime; public Task(String name, String description, Status status) { this.name = name; this.description = description; this.status = status; + this.duration = Duration.ZERO; } public Task(String name, String description, int id, Status status) { @@ -19,6 +24,24 @@ public Task(String name, String description, int id, Status status) { this.description = description; this.id = id; this.status = status; + this.duration = Duration.ZERO; + } + + public Task(String name, String description, Status status, Duration duration, LocalDateTime startTime) { + this.name = name; + this.description = description; + this.status = status; + this.duration = duration; + this.startTime = startTime; + } + + public Task(String name, String description, int id, Status status, Duration duration, LocalDateTime startTime) { + this.name = name; + this.description = description; + this.id = id; + this.status = status; + this.duration = duration; + this.startTime = startTime; } public String getName() { @@ -53,10 +76,37 @@ public void setStatus(Status status) { this.status = status; } + public Duration getDuration() { + return duration; + } + + public void setDuration(Duration duration) { + this.duration = duration; + } + + public LocalDateTime getStartTime() { + return startTime; + } + + public void setStartTime(LocalDateTime startTime) { + this.startTime = startTime; + } + + public LocalDateTime getEndTime() { + if (startTime == null || duration == null) { + return null; + } + return startTime.plus(duration); + } + @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Task task = (Task) o; return id == task.id; } @@ -68,7 +118,7 @@ public int hashCode() { @Override public String toString() { - return String.format("Task{name='%s', description='%s', id=%d, status=%s}", - name, description, id, status); + return String.format("Task{name='%s', description='%s', id=%d, status=%s, duration=%s, startTime=%s}", + name, description, id, status, duration, startTime); } } \ No newline at end of file diff --git a/test/TaskManagerTest.java b/test/TaskManagerTest.java new file mode 100644 index 0000000..a5f7ca8 --- /dev/null +++ b/test/TaskManagerTest.java @@ -0,0 +1,313 @@ + +import manager.TaskManager; +import model.Epic; +import model.Status; +import model.Subtask; +import model.Task; +import org.junit.jupiter.api.Test; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +public abstract class TaskManagerTest { + protected T taskManager; + + @Test + void testIsTasksOverlapping() { + LocalDateTime baseTime = LocalDateTime.now(); + + Task task1 = new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(60), baseTime); + Task task2 = new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusMinutes(30)); + + assertTrue(taskManager.isTasksOverlapping(task1, task2)); + } + + @Test + void testIsTasksNotOverlapping() { + LocalDateTime baseTime = LocalDateTime.now(); + + Task task1 = new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime); + Task task2 = new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusHours(1)); + + assertFalse(taskManager.isTasksOverlapping(task1, task2)); + } + + @Test + void testIsTasksTouchingNotOverlapping() { + LocalDateTime baseTime = LocalDateTime.now(); + + Task task1 = new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime); + Task task2 = new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusMinutes(30)); + + assertFalse(taskManager.isTasksOverlapping(task1, task2)); + } + + @Test + void testHasAnyTimeOverlaps() { + LocalDateTime baseTime = LocalDateTime.now(); + + taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(60), baseTime)); + taskManager.createTask(new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusMinutes(30))); + + assertTrue(taskManager.hasAnyTimeOverlaps()); + } + + @Test + void testHasNoTimeOverlaps() { + LocalDateTime baseTime = LocalDateTime.now(); + + taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime)); + taskManager.createTask(new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusHours(1))); + + assertFalse(taskManager.hasAnyTimeOverlaps()); + } + + @Test + void testGetTaskByIdOptional() { + Task task = taskManager.createTask(new Task("Test", "Description", Status.NEW)); + + Optional result = taskManager.getTaskByIdOptional(task.getId()); + assertTrue(result.isPresent()); + assertEquals(task.getId(), result.get().getId()); + } + + @Test + void testGetTaskByIdOptionalNotFound() { + Optional result = taskManager.getTaskByIdOptional(999); + assertFalse(result.isPresent()); + } + + @Test + void testGetEpicByIdOptional() { + Epic epic = taskManager.createEpic(new Epic("Test", "Description")); + + Optional result = taskManager.getEpicByIdOptional(epic.getId()); + assertTrue(result.isPresent()); + assertEquals(epic.getId(), result.get().getId()); + } + + @Test + void testGetSubtaskByIdOptional() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + Subtask subtask = taskManager.createSubtask(new Subtask("Test", "Description", Status.NEW, epic.getId())); + + Optional result = taskManager.getSubtaskByIdOptional(subtask.getId()); + assertTrue(result.isPresent()); + assertEquals(subtask.getId(), result.get().getId()); + } + + @Test + void getPrioritizedTasks_shouldReturnSortedTasks() { + LocalDateTime now = LocalDateTime.now(); + + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(30), now.plusHours(2))); + Task task2 = taskManager.createTask(new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(45), now.plusHours(1))); + Task task3 = taskManager.createTask(new Task("Task 3", "Description", Status.NEW, + Duration.ofMinutes(15), null)); + + List prioritized = taskManager.getPrioritizedTasks(); + + assertEquals(2, prioritized.size()); + assertEquals(task2.getId(), prioritized.get(0).getId()); + assertEquals(task1.getId(), prioritized.get(1).getId()); + } + + @Test + void testTaskOverlapping() { + LocalDateTime baseTime = LocalDateTime.now(); + + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(60), baseTime)); + + Task overlappingTask = new Task("Overlapping", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusMinutes(30)); + + assertTrue(taskManager.isTaskOverlapping(overlappingTask)); + + Task nonOverlappingTask = new Task("Non-overlapping", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusHours(2)); + + assertFalse(taskManager.isTaskOverlapping(nonOverlappingTask)); + } + + @Test + void testEpicTimingCalculation() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + + LocalDateTime start1 = LocalDateTime.now(); + LocalDateTime start2 = start1.plusHours(1); + + Subtask subtask1 = taskManager.createSubtask(new Subtask("Sub 1", "Description", Status.NEW, epic.getId(), + Duration.ofMinutes(30), start1)); + Subtask subtask2 = taskManager.createSubtask(new Subtask("Sub 2", "Description", Status.NEW, epic.getId(), + Duration.ofMinutes(45), start2)); + + Epic retrievedEpic = taskManager.getEpicById(epic.getId()); + + assertEquals(Duration.ofMinutes(75), retrievedEpic.getDuration()); + assertEquals(start1, retrievedEpic.getStartTime()); + assertEquals(start2.plusMinutes(45), retrievedEpic.getEndTime()); + } + + @Test + void testTaskWithNullTimeNotInPrioritized() { + Task task = taskManager.createTask(new Task("Task", "Description", Status.NEW, + Duration.ofMinutes(30), null)); + + List prioritized = taskManager.getPrioritizedTasks(); + + assertTrue(prioritized.isEmpty()); + } + + @Test + void testGetEndTime() { + LocalDateTime startTime = LocalDateTime.now(); + Duration duration = Duration.ofMinutes(90); + Task task = new Task("Task", "Description", Status.NEW, duration, startTime); + + LocalDateTime endTime = task.getEndTime(); + + assertEquals(startTime.plusMinutes(90), endTime); + } + + @Test + void testGetEndTimeWithNullStartTime() { + Task task = new Task("Task", "Description", Status.NEW, Duration.ofMinutes(30), null); + + assertNull(task.getEndTime()); + } + + @Test + void testEpicStatusAllNew() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + + taskManager.createSubtask(new Subtask("Sub 1", "Description", Status.NEW, epic.getId())); + taskManager.createSubtask(new Subtask("Sub 2", "Description", Status.NEW, epic.getId())); + + assertEquals(Status.NEW, taskManager.getEpicById(epic.getId()).getStatus()); + } + + @Test + void testEpicStatusAllDone() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + + Subtask sub1 = taskManager.createSubtask(new Subtask("Sub 1", "Description", Status.NEW, epic.getId())); + Subtask sub2 = taskManager.createSubtask(new Subtask("Sub 2", "Description", Status.NEW, epic.getId())); + + taskManager.updateSubtask(new Subtask("Sub 1", "Description", sub1.getId(), Status.DONE, epic.getId())); + taskManager.updateSubtask(new Subtask("Sub 2", "Description", sub2.getId(), Status.DONE, epic.getId())); + + assertEquals(Status.DONE, taskManager.getEpicById(epic.getId()).getStatus()); + } + + @Test + void testEpicStatusMixedNewAndDone() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + + Subtask sub1 = taskManager.createSubtask(new Subtask("Sub 1", "Description", Status.NEW, epic.getId())); + Subtask sub2 = taskManager.createSubtask(new Subtask("Sub 2", "Description", Status.NEW, epic.getId())); + + taskManager.updateSubtask(new Subtask("Sub 1", "Description", sub1.getId(), Status.DONE, epic.getId())); + + assertEquals(Status.IN_PROGRESS, taskManager.getEpicById(epic.getId()).getStatus()); + } + + @Test + void testEpicStatusAllInProgress() { + Epic epic = taskManager.createEpic(new Epic("Epic", "Description")); + + Subtask sub1 = taskManager.createSubtask(new Subtask("Sub 1", "Description", Status.NEW, epic.getId())); + Subtask sub2 = taskManager.createSubtask(new Subtask("Sub 2", "Description", Status.NEW, epic.getId())); + + taskManager.updateSubtask(new Subtask("Sub 1", "Description", sub1.getId(), Status.IN_PROGRESS, epic.getId())); + taskManager.updateSubtask(new Subtask("Sub 2", "Description", sub2.getId(), Status.IN_PROGRESS, epic.getId())); + + assertEquals(Status.IN_PROGRESS, taskManager.getEpicById(epic.getId()).getStatus()); + } + + @Test + void testHistoryManagerEmptyHistory() { + assertTrue(taskManager.getHistory().isEmpty()); + } + + @Test + void testHistoryManagerDuplicates() { + Task task = taskManager.createTask(new Task("Task", "Description", Status.NEW)); + + taskManager.getTaskById(task.getId()); + taskManager.getTaskById(task.getId()); + taskManager.getTaskById(task.getId()); + + assertEquals(1, taskManager.getHistory().size()); + } + + @Test + void testHistoryManagerRemoveFromBeginning() { + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW)); + Task task2 = taskManager.createTask(new Task("Task 2", "Description", Status.NEW)); + Task task3 = taskManager.createTask(new Task("Task 3", "Description", Status.NEW)); + + taskManager.getTaskById(task1.getId()); + taskManager.getTaskById(task2.getId()); + taskManager.getTaskById(task3.getId()); + + taskManager.deleteTaskById(task1.getId()); + + List history = taskManager.getHistory(); + assertEquals(2, history.size()); + assertEquals(task2.getId(), history.get(0).getId()); + assertEquals(task3.getId(), history.get(1).getId()); + } + + @Test + void testHistoryManagerRemoveFromMiddle() { + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW)); + Task task2 = taskManager.createTask(new Task("Task 2", "Description", Status.NEW)); + Task task3 = taskManager.createTask(new Task("Task 3", "Description", Status.NEW)); + + taskManager.getTaskById(task1.getId()); + taskManager.getTaskById(task2.getId()); + taskManager.getTaskById(task3.getId()); + + taskManager.deleteTaskById(task2.getId()); + + List history = taskManager.getHistory(); + assertEquals(2, history.size()); + assertEquals(task1.getId(), history.get(0).getId()); + assertEquals(task3.getId(), history.get(1).getId()); + } + + @Test + void testHistoryManagerRemoveFromEnd() { + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW)); + Task task2 = taskManager.createTask(new Task("Task 2", "Description", Status.NEW)); + Task task3 = taskManager.createTask(new Task("Task 3", "Description", Status.NEW)); + + taskManager.getTaskById(task1.getId()); + taskManager.getTaskById(task2.getId()); + taskManager.getTaskById(task3.getId()); + + taskManager.deleteTaskById(task3.getId()); + + List history = taskManager.getHistory(); + assertEquals(2, history.size()); + assertEquals(task1.getId(), history.get(0).getId()); + assertEquals(task2.getId(), history.get(1).getId()); + } +} \ No newline at end of file diff --git a/test/manager/ExceptionHandlingTest.java b/test/manager/ExceptionHandlingTest.java new file mode 100644 index 0000000..445d384 --- /dev/null +++ b/test/manager/ExceptionHandlingTest.java @@ -0,0 +1,35 @@ +package manager; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +class ExceptionHandlingTest { + + @TempDir + Path tempDir; + + @Test + void managerSaveException_shouldBeThrownOnSaveError() { + File invalidFile = new File("/invalid/path/tasks.csv"); + FileBackedTaskManager manager = new FileBackedTaskManager(invalidFile); + + assertThrows(ManagerSaveException.class, () -> { + manager.save(); + }); + } + + @Test + void noException_withValidFile() { + File validFile = tempDir.resolve("valid.csv").toFile(); + FileBackedTaskManager manager = new FileBackedTaskManager(validFile); + + assertDoesNotThrow(() -> { + manager.save(); + }); + } +} \ No newline at end of file diff --git a/test/manager/FileBackedTest.java b/test/manager/FileBackedTest.java index ce61ca6..a9a682f 100644 --- a/test/manager/FileBackedTest.java +++ b/test/manager/FileBackedTest.java @@ -8,6 +8,9 @@ import java.io.File; import java.nio.file.Path; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -75,4 +78,82 @@ void testLoadFromNonExistentFile() { assertNotNull(manager); assertTrue(manager.getAllTasks().isEmpty()); } + + // Новые тесты для спринта 8 + @Test + void testSaveAndLoadWithTimeFields() { + File file = tempDir.resolve("test_time.csv").toFile(); + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + LocalDateTime startTime = LocalDateTime.now(); + Duration duration = Duration.ofMinutes(45); + + // Создаем задачи с временными полями + Task task = manager.createTask(new Task("Task", "Description", Status.NEW, duration, startTime)); + Epic epic = manager.createEpic(new Epic("Epic", "Description")); + Subtask subtask = manager.createSubtask(new Subtask("Subtask", "Description", Status.NEW, + epic.getId(), Duration.ofMinutes(30), startTime.plusHours(1))); + + // Загружаем из файла + FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(file); + + Task loadedTask = loadedManager.getTaskById(task.getId()); + assertNotNull(loadedTask); + assertEquals(duration, loadedTask.getDuration()); + assertEquals(startTime, loadedTask.getStartTime()); + + Subtask loadedSubtask = loadedManager.getSubtaskById(subtask.getId()); + assertNotNull(loadedSubtask); + assertEquals(Duration.ofMinutes(30), loadedSubtask.getDuration()); + assertEquals(startTime.plusHours(1), loadedSubtask.getStartTime()); + + Epic loadedEpic = loadedManager.getEpicById(epic.getId()); + assertNotNull(loadedEpic); + assertEquals(Duration.ofMinutes(30), loadedEpic.getDuration()); + assertEquals(startTime.plusHours(1), loadedEpic.getStartTime()); + } + + @Test + void testPrioritizedTasksAfterLoad() { + File file = tempDir.resolve("test_prioritized.csv").toFile(); + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + LocalDateTime now = LocalDateTime.now(); + + manager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(30), now.plusHours(2))); + manager.createTask(new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(45), now.plusHours(1))); + + FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(file); + + List prioritized = loadedManager.getPrioritizedTasks(); + assertEquals(2, prioritized.size()); + assertEquals(now.plusHours(1), prioritized.get(0).getStartTime()); + assertEquals(now.plusHours(2), prioritized.get(1).getStartTime()); + } + + @Test + void testSaveAndLoadEpicWithTimeCalculation() { + File file = tempDir.resolve("test_epic_time.csv").toFile(); + FileBackedTaskManager manager = new FileBackedTaskManager(file); + + Epic epic = manager.createEpic(new Epic("Epic", "Description")); + + LocalDateTime start1 = LocalDateTime.of(2023, 1, 1, 10, 0); + LocalDateTime start2 = LocalDateTime.of(2023, 1, 1, 11, 0); + + manager.createSubtask(new Subtask("Sub 1", "Description", Status.NEW, epic.getId(), + Duration.ofMinutes(30), start1)); + manager.createSubtask(new Subtask("Sub 2", "Description", Status.NEW, epic.getId(), + Duration.ofMinutes(45), start2)); + + FileBackedTaskManager loadedManager = FileBackedTaskManager.loadFromFile(file); + + Epic loadedEpic = loadedManager.getEpicById(epic.getId()); + assertNotNull(loadedEpic); + assertEquals(Duration.ofMinutes(75), loadedEpic.getDuration()); + assertEquals(start1, loadedEpic.getStartTime()); + assertEquals(start2.plusMinutes(45), loadedEpic.getEndTime()); + } } \ No newline at end of file diff --git a/test/manager/InMemoryTaskManagerTest.java b/test/manager/InMemoryTaskManagerTest.java index 2fe87f7..9d06c71 100644 --- a/test/manager/InMemoryTaskManagerTest.java +++ b/test/manager/InMemoryTaskManagerTest.java @@ -9,6 +9,8 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.List; +import java.time.Duration; +import java.time.LocalDateTime; class InMemoryTaskManagerTest { private TaskManager taskManager; @@ -395,4 +397,65 @@ void testGetSubtasksBadEpic() { List subtasks = taskManager.getSubtasksByEpicId(999); assertTrue(subtasks.isEmpty()); } + + // Новые тесты для спринта 8 + @Test + void testCreateTaskWithTime() { + LocalDateTime startTime = LocalDateTime.now(); + Duration duration = Duration.ofMinutes(30); + Task task = new Task("Test Task", "Test Description", Status.NEW, duration, startTime); + Task createdTask = taskManager.createTask(task); + + assertNotNull(createdTask); + assertEquals(duration, createdTask.getDuration()); + assertEquals(startTime, createdTask.getStartTime()); + assertEquals(startTime.plus(duration), createdTask.getEndTime()); + } + + @Test + void testGetPrioritizedTasks() { + LocalDateTime now = LocalDateTime.now(); + + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(30), now.plusHours(2))); + Task task2 = taskManager.createTask(new Task("Task 2", "Description", Status.NEW, + Duration.ofMinutes(45), now.plusHours(1))); + Task task3 = taskManager.createTask(new Task("Task 3", "Description", Status.NEW, + Duration.ofMinutes(15), null)); + + List prioritized = taskManager.getPrioritizedTasks(); + + assertEquals(2, prioritized.size()); + assertEquals(task2.getId(), prioritized.get(0).getId()); + assertEquals(task1.getId(), prioritized.get(1).getId()); + } + + @Test + void testTaskOverlapping() { + LocalDateTime baseTime = LocalDateTime.now(); + + Task task1 = taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(60), baseTime)); + + Task overlappingTask = new Task("Overlapping", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusMinutes(30)); + + assertTrue(taskManager.isTaskOverlapping(overlappingTask)); + } + + @Test + void testCreateOverlappingTaskShouldFail() { + LocalDateTime baseTime = LocalDateTime.now(); + + taskManager.createTask(new Task("Task 1", "Description", Status.NEW, + Duration.ofMinutes(60), baseTime)); + + Task overlappingTask = new Task("Overlapping", "Description", Status.NEW, + Duration.ofMinutes(30), baseTime.plusMinutes(30)); + + Task result = taskManager.createTask(overlappingTask); + + assertNull(result); + assertEquals(1, taskManager.getAllTasks().size()); + } } \ No newline at end of file diff --git a/test/model/InMemoryHistoryManagerTest.java b/test/model/InMemoryHistoryManagerTest.java index 3dbf32a..430cfcb 100644 --- a/test/model/InMemoryHistoryManagerTest.java +++ b/test/model/InMemoryHistoryManagerTest.java @@ -89,4 +89,65 @@ void testRemoveNonExistentTask() { void testEmptyHistory() { assertTrue(historyManager.getHistory().isEmpty()); } + + // Новые тесты для спринта 8 + @Test + void testRemoveFromBeginning() { + Task task1 = createTask(1, "Task 1"); + Task task2 = createTask(2, "Task 2"); + Task task3 = createTask(3, "Task 3"); + + historyManager.add(task1); + historyManager.add(task2); + historyManager.add(task3); + + historyManager.remove(1); + + List history = historyManager.getHistory(); + assertEquals(2, history.size()); + assertEquals(task2.getId(), history.get(0).getId()); + assertEquals(task3.getId(), history.get(1).getId()); + } + + @Test + void testRemoveFromMiddle() { + Task task1 = createTask(1, "Task 1"); + Task task2 = createTask(2, "Task 2"); + Task task3 = createTask(3, "Task 3"); + + historyManager.add(task1); + historyManager.add(task2); + historyManager.add(task3); + + historyManager.remove(2); + + List history = historyManager.getHistory(); + assertEquals(2, history.size()); + assertEquals(task1.getId(), history.get(0).getId()); + assertEquals(task3.getId(), history.get(1).getId()); + } + + @Test + void testRemoveFromEnd() { + Task task1 = createTask(1, "Task 1"); + Task task2 = createTask(2, "Task 2"); + Task task3 = createTask(3, "Task 3"); + + historyManager.add(task1); + historyManager.add(task2); + historyManager.add(task3); + + historyManager.remove(3); + + List history = historyManager.getHistory(); + assertEquals(2, history.size()); + assertEquals(task1.getId(), history.get(0).getId()); + assertEquals(task2.getId(), history.get(1).getId()); + } + + private Task createTask(int id, String name) { + Task task = new Task(name, "Description", Status.NEW); + task.setId(id); + return task; + } } \ No newline at end of file diff --git a/test/model/TaskTest.java b/test/model/TaskTest.java deleted file mode 100644 index 6f8b618..0000000 --- a/test/model/TaskTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package model; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; - -class TaskTest { - - @Test - void testSameIdEqual() { - Task task1 = new Task("Task 1", "Description 1", 1, Status.NEW); - Task task2 = new Task("Task 2", "Description 2", 1, Status.DONE); - - assertEquals(task1, task2); - assertEquals(task1.hashCode(), task2.hashCode()); - } - - @Test - void testDifferentIdNotEqual() { - Task task1 = new Task("Task", "Description", 1, Status.NEW); - Task task2 = new Task("Task", "Description", 2, Status.NEW); - - assertNotEquals(task1, task2); - } -} \ No newline at end of file From 53b15b27adb37ff3e7867e08d9d1ed18b0c05402 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Thu, 6 Nov 2025 15:46:30 +0300 Subject: [PATCH 16/21] =?UTF-8?q?feat:=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=BE=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=B4=20checkstyle?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/FileBackedTaskManager.java | 20 ++++++++++---------- src/manager/InMemoryTaskManager.java | 8 ++++---- src/manager/TaskManager.java | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index a1e9a7b..19f07cb 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -111,15 +111,15 @@ protected void save() { } private String taskToString(Task task) { - String durationStr = task.getDuration() != null ? - String.valueOf(task.getDuration().toMinutes()) : ""; - String startTimeStr = task.getStartTime() != null ? - task.getStartTime().format(DATE_TIME_FORMATTER) : ""; + String durationStr = task.getDuration() != null + ? String.valueOf(task.getDuration().toMinutes()) : ""; + String startTimeStr = task.getStartTime() != null + ? task.getStartTime().format(DATE_TIME_FORMATTER) : ""; if (task instanceof Epic) { Epic epic = (Epic) task; - String endTimeStr = epic.getEndTime() != null ? - epic.getEndTime().format(DATE_TIME_FORMATTER) : ""; + String endTimeStr = epic.getEndTime() != null + ? epic.getEndTime().format(DATE_TIME_FORMATTER) : ""; return String.format("%d,EPIC,%s,%s,%s,,%s,%s,%s", task.getId(), task.getName(), @@ -157,10 +157,10 @@ private Task taskFromString(String value) { Status status = Status.valueOf(fields[3]); String description = fields[4]; - Duration duration = fields[6].isEmpty() ? Duration.ZERO : - Duration.ofMinutes(Long.parseLong(fields[6])); - LocalDateTime startTime = fields[7].isEmpty() ? null : - LocalDateTime.parse(fields[7], DATE_TIME_FORMATTER); + Duration duration = fields[6].isEmpty() ? Duration.ZERO + : Duration.ofMinutes(Long.parseLong(fields[6])); + LocalDateTime startTime = fields[7].isEmpty() ? null + : LocalDateTime.parse(fields[7], DATE_TIME_FORMATTER); switch (type) { case TASK: diff --git a/src/manager/InMemoryTaskManager.java b/src/manager/InMemoryTaskManager.java index f48a621..9491856 100644 --- a/src/manager/InMemoryTaskManager.java +++ b/src/manager/InMemoryTaskManager.java @@ -63,13 +63,13 @@ public Optional getSubtaskByIdOptional(int id) { @Override public boolean isTasksOverlapping(Task task1, Task task2) { - if (task1.getStartTime() == null || task2.getStartTime() == null || - task1.getEndTime() == null || task2.getEndTime() == null) { + if (task1.getStartTime() == null || task2.getStartTime() == null + || task1.getEndTime() == null || task2.getEndTime() == null) { return false; } - return !task1.getEndTime().isBefore(task2.getStartTime()) && - !task1.getStartTime().isAfter(task2.getEndTime()); + return !task1.getEndTime().isBefore(task2.getStartTime()) + && !task1.getStartTime().isAfter(task2.getEndTime()); } @Override diff --git a/src/manager/TaskManager.java b/src/manager/TaskManager.java index e9f0f43..e10d5c3 100644 --- a/src/manager/TaskManager.java +++ b/src/manager/TaskManager.java @@ -8,34 +8,56 @@ public interface TaskManager { List getAllTasks(); + void deleteAllTasks(); + Task getTaskById(int id); + Optional getTaskByIdOptional(int id); + Task createTask(Task task); + void updateTask(Task task); + void deleteTaskById(int id); List getAllEpics(); + void deleteAllEpics(); + Epic getEpicById(int id); + Optional getEpicByIdOptional(int id); + Epic createEpic(Epic epic); + void updateEpic(Epic epic); + void deleteEpicById(int id); List getAllSubtasks(); + void deleteAllSubtasks(); + Subtask getSubtaskById(int id); + Optional getSubtaskByIdOptional(int id); + Subtask createSubtask(Subtask subtask); + void updateSubtask(Subtask subtask); + void deleteSubtaskById(int id); List getSubtasksByEpicId(int epicId); + List getHistory(); List getPrioritizedTasks(); + boolean isTaskOverlapping(Task task); + boolean isTasksOverlapping(Task task1, Task task2); + boolean hasAnyTimeOverlaps(); } \ No newline at end of file From cfda4991f7ff847247a0e5edb892239130a8afc5 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 7 Nov 2025 15:58:52 +0300 Subject: [PATCH 17/21] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20System.out.println=20=D0=BD=D0=B0=20String.forma?= =?UTF-8?q?t=20=D0=B2=20FileBackedTaskManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/manager/FileBackedTaskManager.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/manager/FileBackedTaskManager.java b/src/manager/FileBackedTaskManager.java index 19f07cb..fae8d25 100644 --- a/src/manager/FileBackedTaskManager.java +++ b/src/manager/FileBackedTaskManager.java @@ -256,17 +256,17 @@ public static void main(String[] args) { FileBackedTaskManager manager2 = FileBackedTaskManager.loadFromFile(tempFile); - System.out.println("Проверка восстановления данных:"); - System.out.println("Задачи в manager2: " + manager2.getAllTasks().size()); - System.out.println("Эпики в manager2: " + manager2.getAllEpics().size()); - System.out.println("Подзадачи в manager2: " + manager2.getAllSubtasks().size()); - System.out.println("История в manager2: " + manager2.getHistory().size()); - System.out.println("Приоритетные задачи в manager2: " + manager2.getPrioritizedTasks().size()); + System.out.println(String.format("Проверка восстановления данных:")); + System.out.println(String.format("Задачи в manager2: %d", manager2.getAllTasks().size())); + System.out.println(String.format("Эпики в manager2: %d", manager2.getAllEpics().size())); + System.out.println(String.format("Подзадачи в manager2: %d", manager2.getAllSubtasks().size())); + System.out.println(String.format("История в manager2: %d", manager2.getHistory().size())); + System.out.println(String.format("Приоритетные задачи в manager2: %d", manager2.getPrioritizedTasks().size())); tempFile.deleteOnExit(); } catch (IOException e) { - System.err.println("Ошибка при работе с файлом: " + e.getMessage()); + System.err.println(String.format("Ошибка при работе с файлом: %s", e.getMessage())); } } } \ No newline at end of file From 9dec21f03396fc3fd2487dab2d8d4aa953e4338c Mon Sep 17 00:00:00 2001 From: Darya346 Date: Thu, 20 Nov 2025 16:45:28 +0300 Subject: [PATCH 18/21] feat: implement HTTP API for task manager --- .idea/compiler.xml | 13 ++ .idea/encodings.xml | 7 + .idea/jarRepositories.xml | 20 ++ .idea/misc.xml | 8 + java-kanban.iml | 23 +- pom.xml | 50 +++++ src/http/BaseHttpHandler.java | 62 ++++++ src/http/EpicsHandler.java | 116 ++++++++++ src/http/HistoryHandler.java | 31 +++ src/http/HttpTaskServer.java | 51 +++++ src/http/JsonConverter.java | 349 ++++++++++++++++++++++++++++++ src/http/PrioritizedHandler.java | 31 +++ src/http/SubtasksHandler.java | 107 +++++++++ src/http/TasksHandler.java | 109 ++++++++++ test/http/HttpTaskServerTest.java | 153 +++++++++++++ 15 files changed, 1109 insertions(+), 21 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/jarRepositories.xml create mode 100644 pom.xml create mode 100644 src/http/BaseHttpHandler.java create mode 100644 src/http/EpicsHandler.java create mode 100644 src/http/HistoryHandler.java create mode 100644 src/http/HttpTaskServer.java create mode 100644 src/http/JsonConverter.java create mode 100644 src/http/PrioritizedHandler.java create mode 100644 src/http/SubtasksHandler.java create mode 100644 src/http/TasksHandler.java create mode 100644 test/http/HttpTaskServerTest.java diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..54b5b20 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index a9182a4..f88aef9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,13 @@ + + + + diff --git a/java-kanban.iml b/java-kanban.iml index 66c15d2..048fe69 100644 --- a/java-kanban.iml +++ b/java-kanban.iml @@ -1,28 +1,9 @@ - - - + + - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4366965 --- /dev/null +++ b/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + ru.yandex.practicum + java-kanban + 1.0-SNAPSHOT + + + 11 + 11 + UTF-8 + + + + + com.google.code.gson + gson + 2.10.1 + + + org.junit.jupiter + junit-jupiter + 5.9.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 11 + 11 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + + + \ No newline at end of file diff --git a/src/http/BaseHttpHandler.java b/src/http/BaseHttpHandler.java new file mode 100644 index 0000000..306fa94 --- /dev/null +++ b/src/http/BaseHttpHandler.java @@ -0,0 +1,62 @@ +package http; + +import com.sun.net.httpserver.HttpExchange; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class BaseHttpHandler { + + protected void sendText(HttpExchange exchange, String text) throws IOException { + byte[] response = text.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); + exchange.sendResponseHeaders(200, response.length); + exchange.getResponseBody().write(response); + exchange.close(); + } + + protected void sendSuccess(HttpExchange exchange) throws IOException { + exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); + exchange.sendResponseHeaders(201, -1); + exchange.close(); + } + + protected void sendNotFound(HttpExchange exchange) throws IOException { + String response = "{\"error\": \"Объект не найден\"}"; + byte[] bytes = response.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); + exchange.sendResponseHeaders(404, bytes.length); + exchange.getResponseBody().write(bytes); + exchange.close(); + } + + protected void sendHasInteractions(HttpExchange exchange) throws IOException { + String response = "{\"error\": \"Задача пересекается с существующими\"}"; + byte[] bytes = response.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); + exchange.sendResponseHeaders(406, bytes.length); + exchange.getResponseBody().write(bytes); + exchange.close(); + } + + protected void sendInternalError(HttpExchange exchange) throws IOException { + String response = "{\"error\": \"Внутренняя ошибка сервера\"}"; + byte[] bytes = response.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); + exchange.sendResponseHeaders(500, bytes.length); + exchange.getResponseBody().write(bytes); + exchange.close(); + } + + protected void sendBadRequest(HttpExchange exchange, String message) throws IOException { + String response = "{\"error\": \"" + message + "\"}"; + byte[] bytes = response.getBytes(StandardCharsets.UTF_8); + exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); + exchange.sendResponseHeaders(400, bytes.length); + exchange.getResponseBody().write(bytes); + exchange.close(); + } + + protected String readRequestBody(HttpExchange exchange) throws IOException { + return new String(exchange.getRequestBody().readAllBytes(), StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/src/http/EpicsHandler.java b/src/http/EpicsHandler.java new file mode 100644 index 0000000..e7827a0 --- /dev/null +++ b/src/http/EpicsHandler.java @@ -0,0 +1,116 @@ +package http; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import manager.TaskManager; +import model.Epic; +import model.Subtask; +import java.io.IOException; +import java.util.List; + +public class EpicsHandler extends BaseHttpHandler implements HttpHandler { + private final TaskManager taskManager; + + public EpicsHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + + switch (method) { + case "GET": + handleGet(exchange, path); + break; + case "POST": + handlePost(exchange); + break; + case "DELETE": + handleDelete(exchange, path); + break; + default: + sendBadRequest(exchange, "Метод не поддерживается"); + } + } catch (Exception e) { + sendInternalError(exchange); + } + } + + private void handleGet(HttpExchange exchange, String path) throws IOException { + if (path.equals("/epics")) { + List epics = taskManager.getAllEpics(); + String response = JsonConverter.epicsToJson(epics); + sendText(exchange, response); + } else if (path.matches("/epics/\\d+")) { + String[] pathParts = path.split("/"); + int id = Integer.parseInt(pathParts[2]); + + Epic epic = taskManager.getEpicById(id); + if (epic != null) { + String response = JsonConverter.epicToJson(epic); + sendText(exchange, response); + } else { + sendNotFound(exchange); + } + } else if (path.matches("/epics/\\d+/subtasks")) { + String[] pathParts = path.split("/"); + int epicId = Integer.parseInt(pathParts[2]); + + Epic epic = taskManager.getEpicById(epicId); + if (epic != null) { + List subtasks = taskManager.getSubtasksByEpicId(epicId); + String response = JsonConverter.subtasksToJson(subtasks); + sendText(exchange, response); + } else { + sendNotFound(exchange); + } + } else { + sendNotFound(exchange); + } + } + + private void handlePost(HttpExchange exchange) throws IOException { + String body = readRequestBody(exchange); + Epic epic = JsonConverter.epicFromJson(body); + + if (epic == null) { + sendBadRequest(exchange, "Неверный формат эпика"); + return; + } + + if (epic.getId() == 0) { + Epic created = taskManager.createEpic(epic); + if (created != null) { + sendSuccess(exchange); + } else { + sendInternalError(exchange); + } + } else { + taskManager.updateEpic(epic); + sendSuccess(exchange); + } + } + + private void handleDelete(HttpExchange exchange, String path) throws IOException { + if (path.equals("/epics")) { + taskManager.deleteAllEpics(); + sendSuccess(exchange); + } else if (path.matches("/epics/\\d+")) { + String[] pathParts = path.split("/"); + int id = Integer.parseInt(pathParts[2]); + + Epic epic = taskManager.getEpicById(id); + if (epic != null) { + taskManager.deleteEpicById(id); + sendSuccess(exchange); + } else { + sendNotFound(exchange); + } + } else { + sendNotFound(exchange); + } + } +} \ No newline at end of file diff --git a/src/http/HistoryHandler.java b/src/http/HistoryHandler.java new file mode 100644 index 0000000..7c52c6d --- /dev/null +++ b/src/http/HistoryHandler.java @@ -0,0 +1,31 @@ +package http; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import manager.TaskManager; +import model.Task; +import java.io.IOException; +import java.util.List; + +public class HistoryHandler extends BaseHttpHandler implements HttpHandler { + private final TaskManager taskManager; + + public HistoryHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + if ("GET".equals(exchange.getRequestMethod())) { + List history = taskManager.getHistory(); + String response = JsonConverter.tasksToJson(history); + sendText(exchange, response); + } else { + sendBadRequest(exchange, "Метод не поддерживается"); + } + } catch (Exception e) { + sendInternalError(exchange); + } + } +} \ No newline at end of file diff --git a/src/http/HttpTaskServer.java b/src/http/HttpTaskServer.java new file mode 100644 index 0000000..074b3e7 --- /dev/null +++ b/src/http/HttpTaskServer.java @@ -0,0 +1,51 @@ +package http; + +import com.sun.net.httpserver.HttpServer; +import manager.TaskManager; +import manager.Managers; +import java.io.IOException; +import java.net.InetSocketAddress; + +public class HttpTaskServer { + private static final int PORT = 8080; + private final TaskManager taskManager; + private HttpServer server; + + public HttpTaskServer(TaskManager taskManager) { + this.taskManager = taskManager; + } + + public void start() { + try { + server = HttpServer.create(new InetSocketAddress(PORT), 0); + + // Регистрируем обработчики + server.createContext("/tasks", new TasksHandler(taskManager)); + server.createContext("/epics", new EpicsHandler(taskManager)); + server.createContext("/subtasks", new SubtasksHandler(taskManager)); + server.createContext("/history", new HistoryHandler(taskManager)); + server.createContext("/prioritized", new PrioritizedHandler(taskManager)); + + server.start(); + System.out.println("HTTP Task Server запущен на порту " + PORT); + } catch (IOException e) { + throw new RuntimeException("Не удалось запустить HTTP сервер", e); + } + } + + public void stop() { + if (server != null) { + server.stop(0); + System.out.println("HTTP Task Server остановлен"); + } + } + + public static void main(String[] args) { + TaskManager manager = Managers.getDefault(); + HttpTaskServer server = new HttpTaskServer(manager); + server.start(); + + // Добавляем shutdown hook для graceful shutdown + Runtime.getRuntime().addShutdownHook(new Thread(server::stop)); + } +} \ No newline at end of file diff --git a/src/http/JsonConverter.java b/src/http/JsonConverter.java new file mode 100644 index 0000000..cca1d99 --- /dev/null +++ b/src/http/JsonConverter.java @@ -0,0 +1,349 @@ +package http; + +import model.Task; +import model.Epic; +import model.Subtask; +import model.Status; +import java.time.Duration; +import java.time.LocalDateTime; +import java.util.List; + +public class JsonConverter { + + public static String taskToJson(Task task) { + if (task == null) return "null"; + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append("\"id\":").append(task.getId()).append(","); + sb.append("\"name\":\"").append(escapeJson(task.getName())).append("\","); + sb.append("\"description\":\"").append(escapeJson(task.getDescription())).append("\","); + sb.append("\"status\":\"").append(task.getStatus()).append("\","); + sb.append("\"type\":\"TASK\","); + + if (task.getDuration() != null) { + sb.append("\"duration\":").append(task.getDuration().toMinutes()).append(","); + } else { + sb.append("\"duration\":0,"); + } + + if (task.getStartTime() != null) { + sb.append("\"startTime\":\"").append(task.getStartTime()).append("\""); + } else { + sb.append("\"startTime\":null"); + } + + sb.append("}"); + return sb.toString(); + } + + public static String epicToJson(Epic epic) { + if (epic == null) return "null"; + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append("\"id\":").append(epic.getId()).append(","); + sb.append("\"name\":\"").append(escapeJson(epic.getName())).append("\","); + sb.append("\"description\":\"").append(escapeJson(epic.getDescription())).append("\","); + sb.append("\"status\":\"").append(epic.getStatus()).append("\","); + sb.append("\"type\":\"EPIC\","); + + if (epic.getDuration() != null) { + sb.append("\"duration\":").append(epic.getDuration().toMinutes()).append(","); + } else { + sb.append("\"duration\":0,"); + } + + if (epic.getStartTime() != null) { + sb.append("\"startTime\":\"").append(epic.getStartTime()).append("\","); + } else { + sb.append("\"startTime\":null,"); + } + + if (epic.getEndTime() != null) { + sb.append("\"endTime\":\"").append(epic.getEndTime()).append("\","); + } else { + sb.append("\"endTime\":null,"); + } + + sb.append("\"subtaskIds\":").append(epic.getSubtaskIds().toString()); + sb.append("}"); + return sb.toString(); + } + + public static String subtaskToJson(Subtask subtask) { + if (subtask == null) return "null"; + + StringBuilder sb = new StringBuilder(); + sb.append("{"); + sb.append("\"id\":").append(subtask.getId()).append(","); + sb.append("\"name\":\"").append(escapeJson(subtask.getName())).append("\","); + sb.append("\"description\":\"").append(escapeJson(subtask.getDescription())).append("\","); + sb.append("\"status\":\"").append(subtask.getStatus()).append("\","); + sb.append("\"type\":\"SUBTASK\","); + sb.append("\"epicId\":").append(subtask.getEpicId()).append(","); + + if (subtask.getDuration() != null) { + sb.append("\"duration\":").append(subtask.getDuration().toMinutes()).append(","); + } else { + sb.append("\"duration\":0,"); + } + + if (subtask.getStartTime() != null) { + sb.append("\"startTime\":\"").append(subtask.getStartTime()).append("\""); + } else { + sb.append("\"startTime\":null"); + } + + sb.append("}"); + return sb.toString(); + } + + public static String tasksToJson(List tasks) { + if (tasks == null) return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < tasks.size(); i++) { + sb.append(taskToJson(tasks.get(i))); + if (i < tasks.size() - 1) { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + public static String epicsToJson(List epics) { + if (epics == null) return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < epics.size(); i++) { + sb.append(epicToJson(epics.get(i))); + if (i < epics.size() - 1) { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + public static String subtasksToJson(List subtasks) { + if (subtasks == null) return "[]"; + + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (int i = 0; i < subtasks.size(); i++) { + sb.append(subtaskToJson(subtasks.get(i))); + if (i < subtasks.size() - 1) { + sb.append(","); + } + } + sb.append("]"); + return sb.toString(); + } + + public static Task taskFromJson(String json) { + try { + if (json == null || json.trim().isEmpty() || json.equals("null")) { + return null; + } + + // Простой парсинг JSON + String cleanJson = json.trim().replaceAll("[{}\"]", ""); + String[] pairs = cleanJson.split(","); + + int id = 0; + String name = ""; + String description = ""; + Status status = Status.NEW; + Duration duration = Duration.ZERO; + LocalDateTime startTime = null; + + for (String pair : pairs) { + String[] keyValue = pair.split(":", 2); + if (keyValue.length != 2) continue; + + String key = keyValue[0].trim(); + String value = keyValue[1].trim(); + + switch (key) { + case "id": + id = Integer.parseInt(value); + break; + case "name": + name = unescapeJson(value); + break; + case "description": + description = unescapeJson(value); + break; + case "status": + status = Status.valueOf(value); + break; + case "duration": + duration = Duration.ofMinutes(Long.parseLong(value)); + break; + case "startTime": + if (!value.equals("null")) { + startTime = LocalDateTime.parse(value); + } + break; + } + } + + if (id == 0) { + return new Task(name, description, status, duration, startTime); + } else { + return new Task(name, description, id, status, duration, startTime); + } + } catch (Exception e) { + return null; + } + } + + public static Epic epicFromJson(String json) { + try { + if (json == null || json.trim().isEmpty() || json.equals("null")) { + return null; + } + + String cleanJson = json.trim().replaceAll("[{}\"]", ""); + String[] pairs = cleanJson.split(","); + + int id = 0; + String name = ""; + String description = ""; + Status status = Status.NEW; + Duration duration = Duration.ZERO; + LocalDateTime startTime = null; + LocalDateTime endTime = null; + + for (String pair : pairs) { + String[] keyValue = pair.split(":", 2); + if (keyValue.length != 2) continue; + + String key = keyValue[0].trim(); + String value = keyValue[1].trim(); + + switch (key) { + case "id": + id = Integer.parseInt(value); + break; + case "name": + name = unescapeJson(value); + break; + case "description": + description = unescapeJson(value); + break; + case "status": + status = Status.valueOf(value); + break; + case "duration": + duration = Duration.ofMinutes(Long.parseLong(value)); + break; + case "startTime": + if (!value.equals("null")) { + startTime = LocalDateTime.parse(value); + } + break; + case "endTime": + if (!value.equals("null")) { + endTime = LocalDateTime.parse(value); + } + break; + } + } + + Epic epic; + if (id == 0) { + epic = new Epic(name, description); + } else { + epic = new Epic(name, description, id, status, duration, startTime, endTime); + } + + return epic; + } catch (Exception e) { + return null; + } + } + + public static Subtask subtaskFromJson(String json) { + try { + if (json == null || json.trim().isEmpty() || json.equals("null")) { + return null; + } + + String cleanJson = json.trim().replaceAll("[{}\"]", ""); + String[] pairs = cleanJson.split(","); + + int id = 0; + String name = ""; + String description = ""; + Status status = Status.NEW; + int epicId = 0; + Duration duration = Duration.ZERO; + LocalDateTime startTime = null; + + for (String pair : pairs) { + String[] keyValue = pair.split(":", 2); + if (keyValue.length != 2) continue; + + String key = keyValue[0].trim(); + String value = keyValue[1].trim(); + + switch (key) { + case "id": + id = Integer.parseInt(value); + break; + case "name": + name = unescapeJson(value); + break; + case "description": + description = unescapeJson(value); + break; + case "status": + status = Status.valueOf(value); + break; + case "epicId": + epicId = Integer.parseInt(value); + break; + case "duration": + duration = Duration.ofMinutes(Long.parseLong(value)); + break; + case "startTime": + if (!value.equals("null")) { + startTime = LocalDateTime.parse(value); + } + break; + } + } + + if (id == 0) { + return new Subtask(name, description, status, epicId, duration, startTime); + } else { + return new Subtask(name, description, id, status, epicId, duration, startTime); + } + } catch (Exception e) { + return null; + } + } + + private static String escapeJson(String str) { + if (str == null) return ""; + return str.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + + private static String unescapeJson(String str) { + if (str == null) return ""; + return str.replace("\\\"", "\"") + .replace("\\\\", "\\") + .replace("\\n", "\n") + .replace("\\r", "\r") + .replace("\\t", "\t"); + } +} \ No newline at end of file diff --git a/src/http/PrioritizedHandler.java b/src/http/PrioritizedHandler.java new file mode 100644 index 0000000..158a5b4 --- /dev/null +++ b/src/http/PrioritizedHandler.java @@ -0,0 +1,31 @@ +package http; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import manager.TaskManager; +import model.Task; +import java.io.IOException; +import java.util.List; + +public class PrioritizedHandler extends BaseHttpHandler implements HttpHandler { + private final TaskManager taskManager; + + public PrioritizedHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + if ("GET".equals(exchange.getRequestMethod())) { + List prioritizedTasks = taskManager.getPrioritizedTasks(); + String response = JsonConverter.tasksToJson(prioritizedTasks); + sendText(exchange, response); + } else { + sendBadRequest(exchange, "Метод не поддерживается"); + } + } catch (Exception e) { + sendInternalError(exchange); + } + } +} \ No newline at end of file diff --git a/src/http/SubtasksHandler.java b/src/http/SubtasksHandler.java new file mode 100644 index 0000000..8c32e28 --- /dev/null +++ b/src/http/SubtasksHandler.java @@ -0,0 +1,107 @@ +package http; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import manager.TaskManager; +import model.Subtask; +import java.io.IOException; +import java.util.List; + +public class SubtasksHandler extends BaseHttpHandler implements HttpHandler { + private final TaskManager taskManager; + + public SubtasksHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + + switch (method) { + case "GET": + handleGet(exchange, path); + break; + case "POST": + handlePost(exchange); + break; + case "DELETE": + handleDelete(exchange, path); + break; + default: + sendBadRequest(exchange, "Метод не поддерживается"); + } + } catch (Exception e) { + sendInternalError(exchange); + } + } + + private void handleGet(HttpExchange exchange, String path) throws IOException { + if (path.equals("/subtasks")) { + List subtasks = taskManager.getAllSubtasks(); + String response = JsonConverter.subtasksToJson(subtasks); + sendText(exchange, response); + } else if (path.matches("/subtasks/\\d+")) { + String[] pathParts = path.split("/"); + int id = Integer.parseInt(pathParts[2]); + + Subtask subtask = taskManager.getSubtaskById(id); + if (subtask != null) { + String response = JsonConverter.subtaskToJson(subtask); + sendText(exchange, response); + } else { + sendNotFound(exchange); + } + } else { + sendNotFound(exchange); + } + } + + private void handlePost(HttpExchange exchange) throws IOException { + String body = readRequestBody(exchange); + Subtask subtask = JsonConverter.subtaskFromJson(body); + + if (subtask == null) { + sendBadRequest(exchange, "Неверный формат подзадачи"); + return; + } + + if (subtask.getId() == 0) { + Subtask created = taskManager.createSubtask(subtask); + if (created != null) { + sendSuccess(exchange); + } else { + if (taskManager.isTaskOverlapping(subtask)) { + sendHasInteractions(exchange); + } else { + sendInternalError(exchange); + } + } + } else { + taskManager.updateSubtask(subtask); + sendSuccess(exchange); + } + } + + private void handleDelete(HttpExchange exchange, String path) throws IOException { + if (path.equals("/subtasks")) { + taskManager.deleteAllSubtasks(); + sendSuccess(exchange); + } else if (path.matches("/subtasks/\\d+")) { + String[] pathParts = path.split("/"); + int id = Integer.parseInt(pathParts[2]); + + Subtask subtask = taskManager.getSubtaskById(id); + if (subtask != null) { + taskManager.deleteSubtaskById(id); + sendSuccess(exchange); + } else { + sendNotFound(exchange); + } + } else { + sendNotFound(exchange); + } + } +} \ No newline at end of file diff --git a/src/http/TasksHandler.java b/src/http/TasksHandler.java new file mode 100644 index 0000000..1ae4005 --- /dev/null +++ b/src/http/TasksHandler.java @@ -0,0 +1,109 @@ +package http; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import manager.TaskManager; +import model.Task; +import java.io.IOException; +import java.util.List; + +public class TasksHandler extends BaseHttpHandler implements HttpHandler { + private final TaskManager taskManager; + + public TasksHandler(TaskManager taskManager) { + this.taskManager = taskManager; + } + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String method = exchange.getRequestMethod(); + String path = exchange.getRequestURI().getPath(); + + switch (method) { + case "GET": + handleGet(exchange, path); + break; + case "POST": + handlePost(exchange); + break; + case "DELETE": + handleDelete(exchange, path); + break; + default: + sendBadRequest(exchange, "Метод не поддерживается"); + } + } catch (Exception e) { + sendInternalError(exchange); + } + } + + private void handleGet(HttpExchange exchange, String path) throws IOException { + if (path.equals("/tasks")) { + List tasks = taskManager.getAllTasks(); + String response = JsonConverter.tasksToJson(tasks); + sendText(exchange, response); + } else if (path.matches("/tasks/\\d+")) { + String[] pathParts = path.split("/"); + int id = Integer.parseInt(pathParts[2]); + + Task task = taskManager.getTaskById(id); + if (task != null) { + String response = JsonConverter.taskToJson(task); + sendText(exchange, response); + } else { + sendNotFound(exchange); + } + } else { + sendNotFound(exchange); + } + } + + private void handlePost(HttpExchange exchange) throws IOException { + String body = readRequestBody(exchange); + Task task = JsonConverter.taskFromJson(body); + + if (task == null) { + sendBadRequest(exchange, "Неверный формат задачи"); + return; + } + + if (task.getId() == 0) { + // Создание новой задачи + Task created = taskManager.createTask(task); + if (created != null) { + sendSuccess(exchange); + } else { + if (taskManager.isTaskOverlapping(task)) { + sendHasInteractions(exchange); + } else { + sendInternalError(exchange); + } + } + } else { + // Обновление существующей задачи + taskManager.updateTask(task); + sendSuccess(exchange); + } + } + + private void handleDelete(HttpExchange exchange, String path) throws IOException { + if (path.equals("/tasks")) { + taskManager.deleteAllTasks(); + sendSuccess(exchange); + } else if (path.matches("/tasks/\\d+")) { + String[] pathParts = path.split("/"); + int id = Integer.parseInt(pathParts[2]); + + Task task = taskManager.getTaskById(id); + if (task != null) { + taskManager.deleteTaskById(id); + sendSuccess(exchange); + } else { + sendNotFound(exchange); + } + } else { + sendNotFound(exchange); + } + } +} \ No newline at end of file diff --git a/test/http/HttpTaskServerTest.java b/test/http/HttpTaskServerTest.java new file mode 100644 index 0000000..122ad57 --- /dev/null +++ b/test/http/HttpTaskServerTest.java @@ -0,0 +1,153 @@ +package http; + +import http.HttpTaskServer; +import manager.Managers; +import manager.TaskManager; +import model.Task; +import model.Status; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import java.time.LocalDateTime; + +import static org.junit.jupiter.api.Assertions.*; + +class HttpTaskServerTest { + private TaskManager manager; + private HttpTaskServer server; + private HttpClient client; + + @BeforeEach + void setUp() { + manager = Managers.getDefault(); + server = new HttpTaskServer(manager); + server.start(); + client = HttpClient.newHttpClient(); + } + + @AfterEach + void tearDown() { + server.stop(); + } + + @Test + void testCreateTask() throws Exception { + String taskJson = "{\"name\":\"Test Task\",\"description\":\"Test Description\"," + + "\"status\":\"NEW\",\"duration\":30,\"startTime\":\"2023-01-01T10:00:00\"}"; + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks")) + .POST(HttpRequest.BodyPublishers.ofString(taskJson)) + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(201, response.statusCode()); + assertEquals(1, manager.getAllTasks().size()); + assertEquals("Test Task", manager.getAllTasks().get(0).getName()); + } + + @Test + void testGetTask() throws Exception { + Task task = manager.createTask(new Task("Test", "Desc", Status.NEW)); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks/" + task.getId())) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(200, response.statusCode()); + assertTrue(response.body().contains("Test")); + } + + @Test + void testGetAllTasks() throws Exception { + manager.createTask(new Task("Task 1", "Desc 1", Status.NEW)); + manager.createTask(new Task("Task 2", "Desc 2", Status.IN_PROGRESS)); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(200, response.statusCode()); + assertTrue(response.body().contains("Task 1")); + assertTrue(response.body().contains("Task 2")); + } + + @Test + void testDeleteTask() throws Exception { + Task task = manager.createTask(new Task("Test", "Desc", Status.NEW)); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks/" + task.getId())) + .DELETE() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(201, response.statusCode()); + assertEquals(0, manager.getAllTasks().size()); + } + + @Test + void testGetHistory() throws Exception { + Task task = manager.createTask(new Task("Test", "Desc", Status.NEW)); + manager.getTaskById(task.getId()); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/history")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(200, response.statusCode()); + assertTrue(response.body().contains("Test")); + } + + @Test + void testGetPrioritized() throws Exception { + Task task1 = manager.createTask(new Task("Task 1", "Desc 1", Status.NEW, + Duration.ofMinutes(30), LocalDateTime.now())); + Task task2 = manager.createTask(new Task("Task 2", "Desc 2", Status.NEW, + Duration.ofMinutes(45), LocalDateTime.now().plusHours(1))); + + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/prioritized")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(200, response.statusCode()); + assertTrue(response.body().contains("Task 1")); + assertTrue(response.body().contains("Task 2")); + + // Используем переменные, чтобы убрать предупреждения + assertNotNull(task1); + assertNotNull(task2); + } + + @Test + void testTaskNotFound() throws Exception { + HttpRequest request = HttpRequest.newBuilder() + .uri(URI.create("http://localhost:8080/tasks/999")) + .GET() + .build(); + + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + + assertEquals(404, response.statusCode()); + } +} \ No newline at end of file From 0701bd15d6c3260771c6aa14d247f3de8f14e2f6 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Thu, 20 Nov 2025 16:57:01 +0300 Subject: [PATCH 19/21] feat: update HTTP API implementation --- src/http/HttpTaskServer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http/HttpTaskServer.java b/src/http/HttpTaskServer.java index 074b3e7..dba0105 100644 --- a/src/http/HttpTaskServer.java +++ b/src/http/HttpTaskServer.java @@ -48,4 +48,4 @@ public static void main(String[] args) { // Добавляем shutdown hook для graceful shutdown Runtime.getRuntime().addShutdownHook(new Thread(server::stop)); } -} \ No newline at end of file +} From 4a8b41188d5e08953e0dc59e09830183e5df6f4d Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 21 Nov 2025 19:20:32 +0300 Subject: [PATCH 20/21] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BD=D0=B0=D0=B7=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D1=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/http/JsonConverter.java | 134 ++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/src/http/JsonConverter.java b/src/http/JsonConverter.java index cca1d99..bfb4092 100644 --- a/src/http/JsonConverter.java +++ b/src/http/JsonConverter.java @@ -13,135 +13,135 @@ public class JsonConverter { public static String taskToJson(Task task) { if (task == null) return "null"; - StringBuilder sb = new StringBuilder(); - sb.append("{"); - sb.append("\"id\":").append(task.getId()).append(","); - sb.append("\"name\":\"").append(escapeJson(task.getName())).append("\","); - sb.append("\"description\":\"").append(escapeJson(task.getDescription())).append("\","); - sb.append("\"status\":\"").append(task.getStatus()).append("\","); - sb.append("\"type\":\"TASK\","); + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("{"); + jsonBuilder.append("\"id\":").append(task.getId()).append(","); + jsonBuilder.append("\"name\":\"").append(escapeJson(task.getName())).append("\","); + jsonBuilder.append("\"description\":\"").append(escapeJson(task.getDescription())).append("\","); + jsonBuilder.append("\"status\":\"").append(task.getStatus()).append("\","); + jsonBuilder.append("\"type\":\"TASK\","); if (task.getDuration() != null) { - sb.append("\"duration\":").append(task.getDuration().toMinutes()).append(","); + jsonBuilder.append("\"duration\":").append(task.getDuration().toMinutes()).append(","); } else { - sb.append("\"duration\":0,"); + jsonBuilder.append("\"duration\":0,"); } if (task.getStartTime() != null) { - sb.append("\"startTime\":\"").append(task.getStartTime()).append("\""); + jsonBuilder.append("\"startTime\":\"").append(task.getStartTime()).append("\""); } else { - sb.append("\"startTime\":null"); + jsonBuilder.append("\"startTime\":null"); } - sb.append("}"); - return sb.toString(); + jsonBuilder.append("}"); + return jsonBuilder.toString(); } public static String epicToJson(Epic epic) { if (epic == null) return "null"; - StringBuilder sb = new StringBuilder(); - sb.append("{"); - sb.append("\"id\":").append(epic.getId()).append(","); - sb.append("\"name\":\"").append(escapeJson(epic.getName())).append("\","); - sb.append("\"description\":\"").append(escapeJson(epic.getDescription())).append("\","); - sb.append("\"status\":\"").append(epic.getStatus()).append("\","); - sb.append("\"type\":\"EPIC\","); + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("{"); + jsonBuilder.append("\"id\":").append(epic.getId()).append(","); + jsonBuilder.append("\"name\":\"").append(escapeJson(epic.getName())).append("\","); + jsonBuilder.append("\"description\":\"").append(escapeJson(epic.getDescription())).append("\","); + jsonBuilder.append("\"status\":\"").append(epic.getStatus()).append("\","); + jsonBuilder.append("\"type\":\"EPIC\","); if (epic.getDuration() != null) { - sb.append("\"duration\":").append(epic.getDuration().toMinutes()).append(","); + jsonBuilder.append("\"duration\":").append(epic.getDuration().toMinutes()).append(","); } else { - sb.append("\"duration\":0,"); + jsonBuilder.append("\"duration\":0,"); } if (epic.getStartTime() != null) { - sb.append("\"startTime\":\"").append(epic.getStartTime()).append("\","); + jsonBuilder.append("\"startTime\":\"").append(epic.getStartTime()).append("\","); } else { - sb.append("\"startTime\":null,"); + jsonBuilder.append("\"startTime\":null,"); } if (epic.getEndTime() != null) { - sb.append("\"endTime\":\"").append(epic.getEndTime()).append("\","); + jsonBuilder.append("\"endTime\":\"").append(epic.getEndTime()).append("\","); } else { - sb.append("\"endTime\":null,"); + jsonBuilder.append("\"endTime\":null,"); } - sb.append("\"subtaskIds\":").append(epic.getSubtaskIds().toString()); - sb.append("}"); - return sb.toString(); + jsonBuilder.append("\"subtaskIds\":").append(epic.getSubtaskIds().toString()); + jsonBuilder.append("}"); + return jsonBuilder.toString(); } public static String subtaskToJson(Subtask subtask) { if (subtask == null) return "null"; - StringBuilder sb = new StringBuilder(); - sb.append("{"); - sb.append("\"id\":").append(subtask.getId()).append(","); - sb.append("\"name\":\"").append(escapeJson(subtask.getName())).append("\","); - sb.append("\"description\":\"").append(escapeJson(subtask.getDescription())).append("\","); - sb.append("\"status\":\"").append(subtask.getStatus()).append("\","); - sb.append("\"type\":\"SUBTASK\","); - sb.append("\"epicId\":").append(subtask.getEpicId()).append(","); + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("{"); + jsonBuilder.append("\"id\":").append(subtask.getId()).append(","); + jsonBuilder.append("\"name\":\"").append(escapeJson(subtask.getName())).append("\","); + jsonBuilder.append("\"description\":\"").append(escapeJson(subtask.getDescription())).append("\","); + jsonBuilder.append("\"status\":\"").append(subtask.getStatus()).append("\","); + jsonBuilder.append("\"type\":\"SUBTASK\","); + jsonBuilder.append("\"epicId\":").append(subtask.getEpicId()).append(","); if (subtask.getDuration() != null) { - sb.append("\"duration\":").append(subtask.getDuration().toMinutes()).append(","); + jsonBuilder.append("\"duration\":").append(subtask.getDuration().toMinutes()).append(","); } else { - sb.append("\"duration\":0,"); + jsonBuilder.append("\"duration\":0,"); } if (subtask.getStartTime() != null) { - sb.append("\"startTime\":\"").append(subtask.getStartTime()).append("\""); + jsonBuilder.append("\"startTime\":\"").append(subtask.getStartTime()).append("\""); } else { - sb.append("\"startTime\":null"); + jsonBuilder.append("\"startTime\":null"); } - sb.append("}"); - return sb.toString(); + jsonBuilder.append("}"); + return jsonBuilder.toString(); } public static String tasksToJson(List tasks) { if (tasks == null) return "[]"; - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < tasks.size(); i++) { - sb.append(taskToJson(tasks.get(i))); - if (i < tasks.size() - 1) { - sb.append(","); + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("["); + for (int taskIndex = 0; taskIndex < tasks.size(); taskIndex++) { + jsonBuilder.append(taskToJson(tasks.get(taskIndex))); + if (taskIndex < tasks.size() - 1) { + jsonBuilder.append(","); } } - sb.append("]"); - return sb.toString(); + jsonBuilder.append("]"); + return jsonBuilder.toString(); } public static String epicsToJson(List epics) { if (epics == null) return "[]"; - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < epics.size(); i++) { - sb.append(epicToJson(epics.get(i))); - if (i < epics.size() - 1) { - sb.append(","); + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("["); + for (int epicIndex = 0; epicIndex < epics.size(); epicIndex++) { + jsonBuilder.append(epicToJson(epics.get(epicIndex))); + if (epicIndex < epics.size() - 1) { + jsonBuilder.append(","); } } - sb.append("]"); - return sb.toString(); + jsonBuilder.append("]"); + return jsonBuilder.toString(); } public static String subtasksToJson(List subtasks) { if (subtasks == null) return "[]"; - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (int i = 0; i < subtasks.size(); i++) { - sb.append(subtaskToJson(subtasks.get(i))); - if (i < subtasks.size() - 1) { - sb.append(","); + StringBuilder jsonBuilder = new StringBuilder(); + jsonBuilder.append("["); + for (int subtaskIndex = 0; subtaskIndex < subtasks.size(); subtaskIndex++) { + jsonBuilder.append(subtaskToJson(subtasks.get(subtaskIndex))); + if (subtaskIndex < subtasks.size() - 1) { + jsonBuilder.append(","); } } - sb.append("]"); - return sb.toString(); + jsonBuilder.append("]"); + return jsonBuilder.toString(); } public static Task taskFromJson(String json) { From 611c754bf3fa9900b2968b6a3aa907847d17b886 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 21 Nov 2025 23:33:12 +0300 Subject: [PATCH 21/21] refactor: use String.format consistently for all string formatting --- src/http/BaseHttpHandler.java | 8 +- src/http/HttpTaskServer.java | 13 ++-- src/http/JsonConverter.java | 142 +++++++++++++++++----------------- src/http/TasksHandler.java | 20 ++--- 4 files changed, 92 insertions(+), 91 deletions(-) diff --git a/src/http/BaseHttpHandler.java b/src/http/BaseHttpHandler.java index 306fa94..57988a9 100644 --- a/src/http/BaseHttpHandler.java +++ b/src/http/BaseHttpHandler.java @@ -21,7 +21,7 @@ protected void sendSuccess(HttpExchange exchange) throws IOException { } protected void sendNotFound(HttpExchange exchange) throws IOException { - String response = "{\"error\": \"Объект не найден\"}"; + String response = String.format("{\"error\": \"%s\"}", "Объект не найден"); byte[] bytes = response.getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); exchange.sendResponseHeaders(404, bytes.length); @@ -30,7 +30,7 @@ protected void sendNotFound(HttpExchange exchange) throws IOException { } protected void sendHasInteractions(HttpExchange exchange) throws IOException { - String response = "{\"error\": \"Задача пересекается с существующими\"}"; + String response = String.format("{\"error\": \"%s\"}", "Задача пересекается с существующими"); byte[] bytes = response.getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); exchange.sendResponseHeaders(406, bytes.length); @@ -39,7 +39,7 @@ protected void sendHasInteractions(HttpExchange exchange) throws IOException { } protected void sendInternalError(HttpExchange exchange) throws IOException { - String response = "{\"error\": \"Внутренняя ошибка сервера\"}"; + String response = String.format("{\"error\": \"%s\"}", "Внутренняя ошибка сервера"); byte[] bytes = response.getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); exchange.sendResponseHeaders(500, bytes.length); @@ -48,7 +48,7 @@ protected void sendInternalError(HttpExchange exchange) throws IOException { } protected void sendBadRequest(HttpExchange exchange, String message) throws IOException { - String response = "{\"error\": \"" + message + "\"}"; + String response = String.format("{\"error\": \"%s\"}", message); byte[] bytes = response.getBytes(StandardCharsets.UTF_8); exchange.getResponseHeaders().add("Content-Type", "application/json;charset=utf-8"); exchange.sendResponseHeaders(400, bytes.length); diff --git a/src/http/HttpTaskServer.java b/src/http/HttpTaskServer.java index dba0105..0bff963 100644 --- a/src/http/HttpTaskServer.java +++ b/src/http/HttpTaskServer.java @@ -27,20 +27,21 @@ public void start() { server.createContext("/prioritized", new PrioritizedHandler(taskManager)); server.start(); - System.out.println("HTTP Task Server запущен на порту " + PORT); - } catch (IOException e) { - throw new RuntimeException("Не удалось запустить HTTP сервер", e); + System.out.println(String.format("HTTP Task Server запущен на порту %d", PORT)); + System.out.println(String.format("Доступные эндпоинты: /tasks, /epics, /subtasks, /history, /prioritized")); + } catch (IOException exception) { + throw new RuntimeException(String.format("Не удалось запустить HTTP сервер на порту %d", PORT), exception); } } public void stop() { if (server != null) { server.stop(0); - System.out.println("HTTP Task Server остановлен"); + System.out.println(String.format("HTTP Task Server остановлен")); } } - public static void main(String[] args) { + public static void main(String[] arguments) { TaskManager manager = Managers.getDefault(); HttpTaskServer server = new HttpTaskServer(manager); server.start(); @@ -48,4 +49,4 @@ public static void main(String[] args) { // Добавляем shutdown hook для graceful shutdown Runtime.getRuntime().addShutdownHook(new Thread(server::stop)); } -} +} \ No newline at end of file diff --git a/src/http/JsonConverter.java b/src/http/JsonConverter.java index bfb4092..fefd1f5 100644 --- a/src/http/JsonConverter.java +++ b/src/http/JsonConverter.java @@ -152,17 +152,17 @@ public static Task taskFromJson(String json) { // Простой парсинг JSON String cleanJson = json.trim().replaceAll("[{}\"]", ""); - String[] pairs = cleanJson.split(","); + String[] keyValuePairs = cleanJson.split(","); - int id = 0; - String name = ""; - String description = ""; - Status status = Status.NEW; - Duration duration = Duration.ZERO; - LocalDateTime startTime = null; + int taskId = 0; + String taskName = ""; + String taskDescription = ""; + Status taskStatus = Status.NEW; + Duration taskDuration = Duration.ZERO; + LocalDateTime taskStartTime = null; - for (String pair : pairs) { - String[] keyValue = pair.split(":", 2); + for (String keyValuePair : keyValuePairs) { + String[] keyValue = keyValuePair.split(":", 2); if (keyValue.length != 2) continue; String key = keyValue[0].trim(); @@ -170,34 +170,34 @@ public static Task taskFromJson(String json) { switch (key) { case "id": - id = Integer.parseInt(value); + taskId = Integer.parseInt(value); break; case "name": - name = unescapeJson(value); + taskName = unescapeJson(value); break; case "description": - description = unescapeJson(value); + taskDescription = unescapeJson(value); break; case "status": - status = Status.valueOf(value); + taskStatus = Status.valueOf(value); break; case "duration": - duration = Duration.ofMinutes(Long.parseLong(value)); + taskDuration = Duration.ofMinutes(Long.parseLong(value)); break; case "startTime": if (!value.equals("null")) { - startTime = LocalDateTime.parse(value); + taskStartTime = LocalDateTime.parse(value); } break; } } - if (id == 0) { - return new Task(name, description, status, duration, startTime); + if (taskId == 0) { + return new Task(taskName, taskDescription, taskStatus, taskDuration, taskStartTime); } else { - return new Task(name, description, id, status, duration, startTime); + return new Task(taskName, taskDescription, taskId, taskStatus, taskDuration, taskStartTime); } - } catch (Exception e) { + } catch (Exception exception) { return null; } } @@ -209,18 +209,18 @@ public static Epic epicFromJson(String json) { } String cleanJson = json.trim().replaceAll("[{}\"]", ""); - String[] pairs = cleanJson.split(","); - - int id = 0; - String name = ""; - String description = ""; - Status status = Status.NEW; - Duration duration = Duration.ZERO; - LocalDateTime startTime = null; - LocalDateTime endTime = null; - - for (String pair : pairs) { - String[] keyValue = pair.split(":", 2); + String[] keyValuePairs = cleanJson.split(","); + + int epicId = 0; + String epicName = ""; + String epicDescription = ""; + Status epicStatus = Status.NEW; + Duration epicDuration = Duration.ZERO; + LocalDateTime epicStartTime = null; + LocalDateTime epicEndTime = null; + + for (String keyValuePair : keyValuePairs) { + String[] keyValue = keyValuePair.split(":", 2); if (keyValue.length != 2) continue; String key = keyValue[0].trim(); @@ -228,42 +228,42 @@ public static Epic epicFromJson(String json) { switch (key) { case "id": - id = Integer.parseInt(value); + epicId = Integer.parseInt(value); break; case "name": - name = unescapeJson(value); + epicName = unescapeJson(value); break; case "description": - description = unescapeJson(value); + epicDescription = unescapeJson(value); break; case "status": - status = Status.valueOf(value); + epicStatus = Status.valueOf(value); break; case "duration": - duration = Duration.ofMinutes(Long.parseLong(value)); + epicDuration = Duration.ofMinutes(Long.parseLong(value)); break; case "startTime": if (!value.equals("null")) { - startTime = LocalDateTime.parse(value); + epicStartTime = LocalDateTime.parse(value); } break; case "endTime": if (!value.equals("null")) { - endTime = LocalDateTime.parse(value); + epicEndTime = LocalDateTime.parse(value); } break; } } Epic epic; - if (id == 0) { - epic = new Epic(name, description); + if (epicId == 0) { + epic = new Epic(epicName, epicDescription); } else { - epic = new Epic(name, description, id, status, duration, startTime, endTime); + epic = new Epic(epicName, epicDescription, epicId, epicStatus, epicDuration, epicStartTime, epicEndTime); } return epic; - } catch (Exception e) { + } catch (Exception exception) { return null; } } @@ -275,18 +275,18 @@ public static Subtask subtaskFromJson(String json) { } String cleanJson = json.trim().replaceAll("[{}\"]", ""); - String[] pairs = cleanJson.split(","); - - int id = 0; - String name = ""; - String description = ""; - Status status = Status.NEW; - int epicId = 0; - Duration duration = Duration.ZERO; - LocalDateTime startTime = null; - - for (String pair : pairs) { - String[] keyValue = pair.split(":", 2); + String[] keyValuePairs = cleanJson.split(","); + + int subtaskId = 0; + String subtaskName = ""; + String subtaskDescription = ""; + Status subtaskStatus = Status.NEW; + int subtaskEpicId = 0; + Duration subtaskDuration = Duration.ZERO; + LocalDateTime subtaskStartTime = null; + + for (String keyValuePair : keyValuePairs) { + String[] keyValue = keyValuePair.split(":", 2); if (keyValue.length != 2) continue; String key = keyValue[0].trim(); @@ -294,53 +294,53 @@ public static Subtask subtaskFromJson(String json) { switch (key) { case "id": - id = Integer.parseInt(value); + subtaskId = Integer.parseInt(value); break; case "name": - name = unescapeJson(value); + subtaskName = unescapeJson(value); break; case "description": - description = unescapeJson(value); + subtaskDescription = unescapeJson(value); break; case "status": - status = Status.valueOf(value); + subtaskStatus = Status.valueOf(value); break; case "epicId": - epicId = Integer.parseInt(value); + subtaskEpicId = Integer.parseInt(value); break; case "duration": - duration = Duration.ofMinutes(Long.parseLong(value)); + subtaskDuration = Duration.ofMinutes(Long.parseLong(value)); break; case "startTime": if (!value.equals("null")) { - startTime = LocalDateTime.parse(value); + subtaskStartTime = LocalDateTime.parse(value); } break; } } - if (id == 0) { - return new Subtask(name, description, status, epicId, duration, startTime); + if (subtaskId == 0) { + return new Subtask(subtaskName, subtaskDescription, subtaskStatus, subtaskEpicId, subtaskDuration, subtaskStartTime); } else { - return new Subtask(name, description, id, status, epicId, duration, startTime); + return new Subtask(subtaskName, subtaskDescription, subtaskId, subtaskStatus, subtaskEpicId, subtaskDuration, subtaskStartTime); } - } catch (Exception e) { + } catch (Exception exception) { return null; } } - private static String escapeJson(String str) { - if (str == null) return ""; - return str.replace("\\", "\\\\") + private static String escapeJson(String inputString) { + if (inputString == null) return ""; + return inputString.replace("\\", "\\\\") .replace("\"", "\\\"") .replace("\n", "\\n") .replace("\r", "\\r") .replace("\t", "\\t"); } - private static String unescapeJson(String str) { - if (str == null) return ""; - return str.replace("\\\"", "\"") + private static String unescapeJson(String inputString) { + if (inputString == null) return ""; + return inputString.replace("\\\"", "\"") .replace("\\\\", "\\") .replace("\\n", "\n") .replace("\\r", "\r") diff --git a/src/http/TasksHandler.java b/src/http/TasksHandler.java index 1ae4005..e907428 100644 --- a/src/http/TasksHandler.java +++ b/src/http/TasksHandler.java @@ -33,7 +33,7 @@ public void handle(HttpExchange exchange) throws IOException { default: sendBadRequest(exchange, "Метод не поддерживается"); } - } catch (Exception e) { + } catch (Exception exception) { sendInternalError(exchange); } } @@ -45,9 +45,9 @@ private void handleGet(HttpExchange exchange, String path) throws IOException { sendText(exchange, response); } else if (path.matches("/tasks/\\d+")) { String[] pathParts = path.split("/"); - int id = Integer.parseInt(pathParts[2]); + int taskId = Integer.parseInt(pathParts[2]); - Task task = taskManager.getTaskById(id); + Task task = taskManager.getTaskById(taskId); if (task != null) { String response = JsonConverter.taskToJson(task); sendText(exchange, response); @@ -60,8 +60,8 @@ private void handleGet(HttpExchange exchange, String path) throws IOException { } private void handlePost(HttpExchange exchange) throws IOException { - String body = readRequestBody(exchange); - Task task = JsonConverter.taskFromJson(body); + String requestBody = readRequestBody(exchange); + Task task = JsonConverter.taskFromJson(requestBody); if (task == null) { sendBadRequest(exchange, "Неверный формат задачи"); @@ -70,8 +70,8 @@ private void handlePost(HttpExchange exchange) throws IOException { if (task.getId() == 0) { // Создание новой задачи - Task created = taskManager.createTask(task); - if (created != null) { + Task createdTask = taskManager.createTask(task); + if (createdTask != null) { sendSuccess(exchange); } else { if (taskManager.isTaskOverlapping(task)) { @@ -93,11 +93,11 @@ private void handleDelete(HttpExchange exchange, String path) throws IOException sendSuccess(exchange); } else if (path.matches("/tasks/\\d+")) { String[] pathParts = path.split("/"); - int id = Integer.parseInt(pathParts[2]); + int taskId = Integer.parseInt(pathParts[2]); - Task task = taskManager.getTaskById(id); + Task task = taskManager.getTaskById(taskId); if (task != null) { - taskManager.deleteTaskById(id); + taskManager.deleteTaskById(taskId); sendSuccess(exchange); } else { sendNotFound(exchange);