+ * Позволяет задать пользовательское имя столбца в итоговом файле. + * Если имя не указано, рекомендуется использовать имя самого поля. + *
+ * + * @author ZhelnovachevRoman + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface ColumnCsv { + + /** + * Имя колонки в CSV-файле. + * + * @return имя колонки + */ + String name() default ""; +} diff --git a/src/main/java/org/writer/exception/EmptyListException.java b/src/main/java/org/writer/exception/EmptyListException.java new file mode 100644 index 0000000..2012468 --- /dev/null +++ b/src/main/java/org/writer/exception/EmptyListException.java @@ -0,0 +1,7 @@ +package org.writer.exception; + +public class EmptyListException extends RuntimeException { + public EmptyListException(String message) { + super(message); + } +} diff --git a/src/main/java/org/writer/exception/FileWritingException.java b/src/main/java/org/writer/exception/FileWritingException.java new file mode 100644 index 0000000..c53b1f1 --- /dev/null +++ b/src/main/java/org/writer/exception/FileWritingException.java @@ -0,0 +1,7 @@ +package org.writer.exception; + +public class FileWritingException extends RuntimeException { + public FileWritingException(String fileName) { + super("Ошибка записи файла " + fileName); + } +} diff --git a/src/main/java/org/writer/model/Person.java b/src/main/java/org/writer/model/Person.java index ba8576b..b2b1ab9 100644 --- a/src/main/java/org/writer/model/Person.java +++ b/src/main/java/org/writer/model/Person.java @@ -3,20 +3,26 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import org.writer.annotation.ColumnCsv; @Data @Builder @AllArgsConstructor public class Person { + @ColumnCsv(name = "имя") private String firstName; + @ColumnCsv(name = "фамилия") private String lastName; + @ColumnCsv(name = "день_рождения") private int dayOfBirth; + @ColumnCsv(name = "месяц_рождения") private Months monthOfBirth; + @ColumnCsv(name = "год_рождения") private int yearOfBirth; } diff --git a/src/main/java/org/writer/model/Student.java b/src/main/java/org/writer/model/Student.java index 2b549c4..6a105f4 100644 --- a/src/main/java/org/writer/model/Student.java +++ b/src/main/java/org/writer/model/Student.java @@ -3,6 +3,7 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import org.writer.annotation.ColumnCsv; import java.util.List; @@ -11,7 +12,9 @@ @AllArgsConstructor public class Student { + @ColumnCsv(name = "имя") private String name; + @ColumnCsv(name = "успеваемость") private List+ * Класс предназначен для сохранения коллекций объектов в виде таблицы CSV. + * Если значение поля является {@link List}, элементы списка объединяются через «;». + *
+ * + * @author ZhelnovachevRoman + */ +public class CsvWriter implements Writable { + + @Override + public void writeToFile(List> data, + String fileName) { + if (data == null || data.isEmpty()) { + throw new EmptyListException("Список пуст"); + } + Class> ourClass = data.get(0) + .getClass(); + Field[] fields = ourClass.getDeclaredFields(); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) { + String header = headerConstructor(fields); + writer.write(header); + writer.newLine(); + for (Object object : data) { + String line = lineConstructor(object, + fields); + writer.write(line); + writer.newLine(); + } + } catch (IOException e){ + throw new FileWritingException(fileName); + } + } + + /** + * Формирует строку заголовков CSV на основе аннотаций {@link ColumnCsv}. + * + * @param fields массив полей класса, извлечённых через Reflection. + * @return строка заголовков, объединённая через запятую. + */ + private String headerConstructor(Field[] fields){ + return Arrays.stream(fields) + .map(field -> { + ColumnCsv annotation = field.getAnnotation(ColumnCsv.class); + return annotation.name(); + }) + .collect(Collectors.joining(",")); + } + + /** + * Формирует строку данных для одного объекта. + * Если поле содержит список, его элементы соединяются через «;». + * + * @param object объект, из которого извлекаются значения. + * @param fields массив полей класса, доступных для чтения. + * @return строка данных, объединённая через запятую. + */ + private String lineConstructor(Object object, Field[] fields) { + return Arrays.stream(fields) + .map(field -> { + field.setAccessible(true); + Object value; + try { + value = field.get(object); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + if (value instanceof List> list) { + return list.stream() + .map(Object::toString) + .collect(Collectors.joining(";")); + } + return String.valueOf(value); + }) + .collect(Collectors.joining(",")); + } +} diff --git a/src/main/java/org/writer/Writable.java b/src/main/java/org/writer/service/Writable.java similarity index 79% rename from src/main/java/org/writer/Writable.java rename to src/main/java/org/writer/service/Writable.java index 9f60c45..fe1afa9 100644 --- a/src/main/java/org/writer/Writable.java +++ b/src/main/java/org/writer/service/Writable.java @@ -1,4 +1,4 @@ -package org.writer; +package org.writer.service; import java.util.List; diff --git a/src/main/java/org/writer/util/DataGenerator.java b/src/main/java/org/writer/util/DataGenerator.java new file mode 100644 index 0000000..c919e5c --- /dev/null +++ b/src/main/java/org/writer/util/DataGenerator.java @@ -0,0 +1,73 @@ +package org.writer.util; + +import net.datafaker.Faker; +import org.writer.model.Months; +import org.writer.model.Person; +import org.writer.model.Student; + +import java.util.ArrayList; +import java.util.List; + +/** + * Утилитный класс для генерации тестовых данных с использованием библиотеки {@link Faker}. + *+ * Предоставляет методы для создания коллекций объектов {@link Student} и {@link Person} + * с заполненными случайными значениями полями. Используется для тестирования + * и демонстрации функциональности. + *
+ * + * @author ZhelnovachevRoman + */ +public class DataGenerator { + + private static final Faker faker = new Faker(); + + /** + * Генерирует список студентов с случайными именами и оценками. + *+ * Каждый студент получает случайное имя и список из трёх оценок + * в диапазоне от 1 до 5. + *
+ * + * @param count количество студентов для генерации. + * @return список объектов {@link Student} с заполненными полями. + */ + public static List+ * Каждая персона получает случайное имя, фамилию, день рождения (1-28), + * месяц рождения (из перечисления {@link Months}) и год рождения (1900-2025). + *
+ * + * @param count количество персон для генерации. + * @return список объектов {@link Person} с заполненными полями. + */ + public static List