From ba59fa12dac6419014f8cfe652a9c50bc99e42db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D1=80=D1=88=D0=BE=D0=B2=20=D0=92=D0=BB=D0=B0=D0=B4?= =?UTF-8?q?=D0=B8=D1=81=D0=BB=D0=B0=D0=B2?= Date: Sun, 30 Aug 2026 20:34:10 +0700 Subject: [PATCH] feat: reworked classes StudentCsvWriter and StudentCsvReader --- .../project/datasource/StudentCsvReader.java | 79 +++++++++++++++++++ .../project/datasource/StudentCsvWriter.java | 73 +++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 app/src/main/java/ru/project/datasource/StudentCsvReader.java create mode 100644 app/src/main/java/ru/project/datasource/StudentCsvWriter.java diff --git a/app/src/main/java/ru/project/datasource/StudentCsvReader.java b/app/src/main/java/ru/project/datasource/StudentCsvReader.java new file mode 100644 index 0000000..7403caa --- /dev/null +++ b/app/src/main/java/ru/project/datasource/StudentCsvReader.java @@ -0,0 +1,79 @@ +package ru.project.datasource; + +import ru.project.student.Student; +import ru.project.student.StudentBuilder; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; + +public class StudentCsvReader implements Iterator { + private static final String GROUP_NUMBER = "groupNumber"; + private static final String AVERAGE_GRADE = "averageGrade"; + private static final String RECORD_BOOK_NUMBER = "recordBookNumber"; + + private final CSVParser parser; + private final Iterator records; + + public StudentCsvReader(String absoluteFilePath) { + Path path = Paths.get(absoluteFilePath); + if (!path.isAbsolute()) { + throw new IllegalArgumentException("File path must be absolute"); + } + + try { + CSVFormat format = CSVFormat.DEFAULT.builder() + .setHeader() + .setSkipHeaderRecord(true) + .build(); + this.parser = CSVParser.parse(path, StandardCharsets.UTF_8, format); + validateHeader(parser.getHeaderMap()); + this.records = parser.iterator(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public boolean hasNext() { + return records.hasNext(); + } + + @Override + public Student next() { + if (!records.hasNext()) { + throw new NoSuchElementException(); + } + + CSVRecord record = records.next(); + if (record.size() != 3) { + throw new IllegalArgumentException( + "Expected 3 columns, but got " + record.size()); + } + + double averageGrade = Double.parseDouble(record.get(AVERAGE_GRADE)); + return new StudentBuilder() + .setGroupNumber(record.get(GROUP_NUMBER)) + .setAverageGrade(averageGrade) + .setRecordBookNumber(record.get(RECORD_BOOK_NUMBER)) + .build(); + } + + private static void validateHeader(Map headerMap) { + String[] expected = {GROUP_NUMBER, AVERAGE_GRADE, RECORD_BOOK_NUMBER}; + if (headerMap.size() != expected.length + || !headerMap.keySet().containsAll(Arrays.asList(expected))) { + throw new IllegalArgumentException("Invalid CSV header"); + } + } +} diff --git a/app/src/main/java/ru/project/datasource/StudentCsvWriter.java b/app/src/main/java/ru/project/datasource/StudentCsvWriter.java new file mode 100644 index 0000000..c086ec0 --- /dev/null +++ b/app/src/main/java/ru/project/datasource/StudentCsvWriter.java @@ -0,0 +1,73 @@ +package ru.project.datasource; + +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVPrinter; +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import ru.project.student.Student; +import ru.project.collection.MyList; +import ru.project.collection.MyLinkedList; + + +public class StudentCsvWriter{ + private static final String[] HEADER = { + "groupNumber", "averageGrade", "recordBookNumber" + }; + private static final DateTimeFormatter FILE_NAME_FORMAT = + DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm"); + + private final Path requestedPath; + private Path actualPath; + + public StudentCsvWriter(String absoluteFilePath) { + Path path = Paths.get(absoluteFilePath); + if (!path.isAbsolute()) { + throw new IllegalArgumentException("File path must be absolute"); + } + this.requestedPath = path; + } + + public void write(Student student) throws IOException { + Path target = resolveTargetPath(); + boolean fileExists = Files.exists(target); + + StandardOpenOption[] options = fileExists + ? new StandardOpenOption[]{StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND} + : new StandardOpenOption[]{StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE}; + + try (BufferedWriter writer = Files.newBufferedWriter(target, StandardCharsets.UTF_8, options); + CSVPrinter printer = new CSVPrinter(writer, CSVFormat.DEFAULT)) { + if (!fileExists) { + printer.printRecord((Object[]) HEADER); + } + printer.printRecord( + student.getGroupNumber(), + Double.toString(student.getAverageGrade()), + student.getRecordBookNumber() + ); + } + } + + private Path resolveTargetPath() { + if (actualPath != null) { + return actualPath; + } + if (Files.exists(requestedPath)) { + actualPath = requestedPath; + return actualPath; + } + + Path parent = requestedPath.getParent(); + String fileName = "students-" + LocalDateTime.now().format(FILE_NAME_FORMAT) + ".csv"; + actualPath = parent == null ? Paths.get(fileName).toAbsolutePath() : parent.resolve(fileName); + return actualPath; + } +} \ No newline at end of file