diff --git a/resources/tasks.csv b/resources/tasks.csv new file mode 100644 index 0000000..5cdfc28 --- /dev/null +++ b/resources/tasks.csv @@ -0,0 +1 @@ +id,type,name,status,description,epic diff --git a/src/managers/FileBackedTaskManager.java b/src/managers/FileBackedTaskManager.java new file mode 100644 index 0000000..7ce1cc6 --- /dev/null +++ b/src/managers/FileBackedTaskManager.java @@ -0,0 +1,203 @@ +package managers; + +import managers.exceptions.ManagerSaveException; +import model.*; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; + +import static model.Type.*; + +public class FileBackedTaskManager extends InMemoryTaskManager { + + private final File filename; + + public FileBackedTaskManager(File filename) { + this.filename = filename; + } + + public static FileBackedTaskManager loadFromFile(File filename) { + + if (!filename.exists()) { + /* Если файла не существует, то возвращается пустой менеджер + * с возможностью создать файл и записывать в него*/ + return new FileBackedTaskManager(filename); + } + + FileBackedTaskManager manager = new FileBackedTaskManager(filename); + + try (BufferedReader reader = new BufferedReader(new FileReader(filename, StandardCharsets.UTF_8))) { + reader.readLine(); // пропуск первой служебной строки + + /*конечный id, который будет присвоен idCount(далее счетчик) в manager*/ + int maxId = 0; + + while (reader.ready()) { + String line = reader.readLine(); + if (line.isBlank()) { + continue; + } + String[] parts = line.split(","); + + int id = Integer.parseInt(parts[0]); + + maxId = Math.max(maxId, id); // сохраняется максимальный id + + /* менеджер сам присваивает id, + * но не знает верного счетчика для каждого таска, + * поэтому счетчик устанавливается на id - 1 (то есть предыдущее значение), + * а уже в методе добавления счетчик увеличивается на один, устанавливая верное значение */ + manager.setIdCount(id - 1); + + Type type = Type.valueOf(parts[1]); + String title = parts[2]; + Status status = Status.valueOf(parts[3]); + String description = parts[4]; + + if (type == TASK) { + manager.addTask(new Task(title, description, status)); + } else if (type == EPIC) { + manager.addEpic(new Epic(title, description, status)); + } else if (type == SUBTASK) { + manager.addSubTask(new SubTask(title, description, status, Integer.parseInt(parts[5]))); + } else { + System.out.println("Такой формат не существует"); + } + } + /*счетчик устанавливается на максимальный найденный id, + * теперь отсчет id всех новых тасков будет от этого значения*/ + manager.setIdCount(maxId); + + } catch (IOException e) { + e.printStackTrace(); + } + + return manager; + } + + private void save() { + + try (BufferedWriter bw = new BufferedWriter(new FileWriter(filename, StandardCharsets.UTF_8))) { + bw.write("id,type,name,status,description,epic"); //первая служебная строка файла + bw.newLine(); + + writeTasks(bw, super.getTasks()); // записываются таски + writeTasks(bw, super.getEpics()); // записываются эпики + writeSubTasks(bw, super.getSubTasks()); // записываются сабтаски + + } catch (IOException e) { + throw new ManagerSaveException("Ошибка сохранения файла", e); + } + } + + private void writeTasks(BufferedWriter bw, ArrayList tasks) throws IOException { + /* Для записи в файл таска или эпика требуются методы, + * которые реализованы в родительском классе Task, + * следовательно, можно воспользоваться дженериком + * и не создавать два одинаковых метода для тасков и эпиков*/ + + for (Task task : tasks) { + String line = String.format("%s,%s,%s,%s,%s", + task.getTaskId(), + task.getType(), + task.getTitle(), + task.getStatus(), + task.getDescription() + ); + + bw.write(line); + bw.newLine(); + } + } + + private void writeSubTasks(BufferedWriter bw, ArrayList subTasks) throws IOException { + /* Для записи сабтаска нам требуется метод getEpicId, + * поэтому метод writeSubTask отдельный*/ + for (SubTask subTask : subTasks) { + String line = String.format("%s,%s,%s,%s,%s,%s", + subTask.getTaskId(), + subTask.getType(), + subTask.getTitle(), + subTask.getStatus(), + subTask.getDescription(), + subTask.getEpicId() + ); + + bw.write(line); + bw.newLine(); + } + } + + @Override + public void addTask(Task task) { + super.addTask(task); + save(); + } + + @Override + public void addEpic(Epic epic) { + super.addEpic(epic); + save(); + } + + @Override + public void addSubTask(SubTask subTask) { + super.addSubTask(subTask); + save(); + } + + @Override + public void updateTask(Task task) { + super.updateTask(task); + save(); + } + + @Override + public void updateEpic(Epic epic) { + super.updateTask(epic); + save(); + } + + @Override + public void updateSubTask(SubTask subTask) { + super.updateSubTask(subTask); + save(); + } + + @Override + public void deleteTask(int id) { + super.deleteTask(id); + save(); + } + + @Override + public void deleteEpic(int id) { + super.deleteEpic(id); + save(); + } + + @Override + public void deleteSubTask(int id) { + super.deleteSubTask(id); + save(); + } + + @Override + public void clearTasks() { + super.clearTasks(); + save(); + } + + @Override + public void clearEpics() { + super.clearEpics(); + save(); + } + + @Override + public void clearSubTasks() { + super.clearSubTasks(); + save(); + } +} diff --git a/src/managers/InMemoryTaskManager.java b/src/managers/InMemoryTaskManager.java index 0c8396a..f79fb1b 100644 --- a/src/managers/InMemoryTaskManager.java +++ b/src/managers/InMemoryTaskManager.java @@ -230,6 +230,14 @@ private void clearHistorySubTasks() { } } + public int getIdCount() { + return idCount; + } + + public void setIdCount(int id) { + idCount = id; + } + @Override public List getHistory() { return historyManager.getHistory(); diff --git a/src/managers/exceptions/ManagerSaveException.java b/src/managers/exceptions/ManagerSaveException.java new file mode 100644 index 0000000..9a00698 --- /dev/null +++ b/src/managers/exceptions/ManagerSaveException.java @@ -0,0 +1,16 @@ +package managers.exceptions; + +public class ManagerSaveException extends RuntimeException { + + public ManagerSaveException() { + super(); + } + + public ManagerSaveException(String message) { + super(message); + } + + public ManagerSaveException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/model/Epic.java b/src/model/Epic.java index a9fe2f7..64ac2c9 100644 --- a/src/model/Epic.java +++ b/src/model/Epic.java @@ -29,6 +29,10 @@ public ArrayList getSubtaskIds() { return subtaskIds; } + public Type getType() { + return Type.EPIC; + } + @Override public String toString() { return String.format("%s{id=%d, title=%s, description=%s, status=%s, subtasks=%s}", diff --git a/src/model/SubTask.java b/src/model/SubTask.java index 6f3071c..f008fcd 100644 --- a/src/model/SubTask.java +++ b/src/model/SubTask.java @@ -12,6 +12,10 @@ public int getEpicId() { return epicId; } + public Type getType() { + return Type.SUBTASK; + } + @Override public String toString() { return String.format("%s{id=%d, title=%s, description=%s, status=%s, epicId=%d}", diff --git a/src/model/Task.java b/src/model/Task.java index f1ba32b..fb40b07 100644 --- a/src/model/Task.java +++ b/src/model/Task.java @@ -47,6 +47,9 @@ public void setStatus(Status status) { this.status = status; } + public Type getType() { + return Type.TASK; + } @Override public String toString() { diff --git a/src/model/Type.java b/src/model/Type.java new file mode 100644 index 0000000..cfa6872 --- /dev/null +++ b/src/model/Type.java @@ -0,0 +1,7 @@ +package model; + +public enum Type { + TASK, + EPIC, + SUBTASK +} \ No newline at end of file diff --git a/test/managers/FileBackedTaskManagerTest.java b/test/managers/FileBackedTaskManagerTest.java new file mode 100644 index 0000000..778bb7a --- /dev/null +++ b/test/managers/FileBackedTaskManagerTest.java @@ -0,0 +1,97 @@ +package managers; + +import managers.exceptions.ManagerSaveException; +import model.Epic; +import model.Status; +import model.SubTask; +import model.Task; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import static org.junit.jupiter.api.Assertions.*; + +public class FileBackedTaskManagerTest { + private File tempFile; + private FileBackedTaskManager manager; + + @BeforeEach + public void beforeEach() throws IOException { + tempFile = Files.createTempFile("task", ".csv").toFile(); + manager = new FileBackedTaskManager(tempFile); + } + + @Test + public void shouldSaveAndLoad() { + + Task task1 = new Task("task1", "demo1", Status.NEW); // id=1 + manager.addTask(task1); + + Epic epic1 = new Epic("epic1", "demo1", Status.NEW); // id=2 + manager.addEpic(epic1); + Epic epic2 = new Epic("epic2", "demo2", Status.NEW); // id=3 + manager.addEpic(epic2); + + SubTask subTask1 = new SubTask("subtask1", "demo1", Status.NEW, 2); //id=4 + manager.addSubTask(subTask1); + + Task task2 = new Task("task2", "demo2", Status.NEW); // id=5 + manager.addTask(task2); + + manager.deleteTask(1); + + FileBackedTaskManager manager1 = FileBackedTaskManager.loadFromFile(tempFile); + + assertNull(manager1.getTask(1)); + assertEquals(epic1, manager1.getEpic(2)); + assertEquals(epic2, manager1.getEpic(3)); + assertEquals(subTask1, manager1.getSubTask(4)); + assertEquals(subTask1, manager1.getSubTasksFromEpic(2).getFirst()); + assertEquals(task2, manager1.getTask(5)); + assertEquals(manager.getIdCount(), manager1.getIdCount()); + } + + @Test + public void shouldThrowManagerSaveException() { + manager = new FileBackedTaskManager(new File("blablabla/test.txt")); + + assertThrows(ManagerSaveException.class, + () -> manager.addTask(new Task("title", "demo", Status.NEW))); + } + + @Test + public void shouldReturnEmptyManager() { + FileBackedTaskManager manager1 = FileBackedTaskManager.loadFromFile(new File("resources/test.txt")); + + assertEquals(0, manager1.getIdCount()); + assertEquals(0, manager1.getTasks().size()); + assertEquals(0, manager1.getEpics().size()); + assertEquals(0, manager1.getSubTasks().size()); + } + + @Test + public void shouldSupportUTF8() { + String[] words = { + "Hello", "World", + "Привет", "Мир", + "Selam", "Dünya", + "你好", "世界", + "👋", "🌎" + }; + + for (int i = 0; i < words.length; i += 2) { + manager.addTask(new Task(words[i], words[i + 1], Status.NEW)); + } + + FileBackedTaskManager manager1 = FileBackedTaskManager.loadFromFile(tempFile); + int id = 1; + for (int i = 0; i < words.length; i += 2) { + Task task = manager1.getTask(id++); + assertEquals(words[i], task.getTitle()); + assertEquals(words[i + 1], task.getDescription()); + } + } +} \ No newline at end of file