From 13b5ec64614610b198f613342b39961380db4372 Mon Sep 17 00:00:00 2001 From: gfmx Date: Mon, 24 Nov 2025 02:20:16 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=20=D0=B3=D0=B5=D0=BD=D0=B5=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D0=B8=20CSV=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20?= =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B0=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F=20Writable.jav?= =?UTF-8?q?a,=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D0=B0=D1=8F=20=D1=81=D0=BE?= =?UTF-8?q?=D1=85=D1=80=D0=B0=D0=BD=D1=8F=D0=B5=D1=82=20=D0=BE=D0=B1=D1=8A?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D1=8B=20=D0=B2=20CSV=20=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D0=B0=D1=82=D0=B5=20=D0=B2=20=D1=84=D0=B0=D0=B9=D0=BB,?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=80=D0=B0=D0=B7=D0=BD=D1=8B=D0=B5=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA=D0=B8=20=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D1=82=D0=B0=20=D0=9D=D0=B0=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D1=8B=20=D0=BD=D0=B5=D0=B3=D0=B0=D1=82=D0=B8=D0=B2?= =?UTF-8?q?=D0=BD=D1=8B=D0=B5=20=D0=B8=20=D0=BF=D0=BE=D0=B7=D0=B8=D1=82?= =?UTF-8?q?=D0=B8=D0=B2=D0=BD=D1=8B=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=D0=BB=D0=B0=20=D0=9D=D0=B0=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=20JavaDoc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/writer/Main.java | 40 +++- src/main/java/org/writer/csv/CsvColumn.java | 21 ++ src/main/java/org/writer/csv/CsvFile.java | 18 ++ src/main/java/org/writer/csv/CsvIgnore.java | 14 ++ .../org/writer/exception/CsvException.java | 7 + .../exception/CsvFieldAccessException.java | 7 + .../writer/exception/CsvWriteException.java | 7 + .../org/writer/{ => writer}/Writable.java | 2 +- .../java/org/writer/writer/csv/CsvWriter.java | 215 ++++++++++++++++++ src/test/java/org/writter/CsvWriterTest.java | 65 ++++++ 10 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/writer/csv/CsvColumn.java create mode 100644 src/main/java/org/writer/csv/CsvFile.java create mode 100644 src/main/java/org/writer/csv/CsvIgnore.java create mode 100644 src/main/java/org/writer/exception/CsvException.java create mode 100644 src/main/java/org/writer/exception/CsvFieldAccessException.java create mode 100644 src/main/java/org/writer/exception/CsvWriteException.java rename src/main/java/org/writer/{ => writer}/Writable.java (80%) create mode 100644 src/main/java/org/writer/writer/csv/CsvWriter.java create mode 100644 src/test/java/org/writter/CsvWriterTest.java diff --git a/src/main/java/org/writer/Main.java b/src/main/java/org/writer/Main.java index 40e8213..475b02a 100644 --- a/src/main/java/org/writer/Main.java +++ b/src/main/java/org/writer/Main.java @@ -1,7 +1,43 @@ package org.writer; +import org.writer.model.Months; +import org.writer.model.Person; +import org.writer.model.Student; +import org.writer.writer.Writable; +import org.writer.writer.csv.CsvWriter; + +import java.util.Arrays; +import java.util.List; + public class Main { public static void main(String[] args) { - System.out.println("Hello world!"); + Writable csvWriter = new CsvWriter(); + + List people = Arrays.asList( + Person.builder() + .firstName("Иван") + .lastName("Иванов") + .dayOfBirth(12) + .monthOfBirth(Months.JANUARY) + .yearOfBirth(1997).build(), + Person.builder() + .firstName("Мария") + .lastName("Петрова") + .dayOfBirth(5) + .monthOfBirth(Months.MARCH) + .yearOfBirth(1997).build() + ); + csvWriter.writeToFile(people, "persons.csv"); + + List students = Arrays.asList( + Student.builder() + .name("Алексей") + .score(Arrays.asList("5", "4", "3")).build(), + Student.builder() + .name("Ольга") + .score(Arrays.asList("4", "4", "5")).build() + ); + csvWriter.writeToFile(students, "students.csv"); } -} \ No newline at end of file +} + diff --git a/src/main/java/org/writer/csv/CsvColumn.java b/src/main/java/org/writer/csv/CsvColumn.java new file mode 100644 index 0000000..c885de2 --- /dev/null +++ b/src/main/java/org/writer/csv/CsvColumn.java @@ -0,0 +1,21 @@ +package org.writer.csv; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a field to be included in CSV output with custom configuration. + *

Allows overriding column name, order, and delimiter for collections.

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface CsvColumn { + + String name() default ""; + + int order() default Integer.MAX_VALUE; + + String collectionDelimiter() default ";"; +} diff --git a/src/main/java/org/writer/csv/CsvFile.java b/src/main/java/org/writer/csv/CsvFile.java new file mode 100644 index 0000000..497bad1 --- /dev/null +++ b/src/main/java/org/writer/csv/CsvFile.java @@ -0,0 +1,18 @@ +package org.writer.csv; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines global CSV configuration for a class. + *

Allows setting the delimiter between columns.

+ */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface CsvFile { + + String columnDelimiter() default ","; + +} diff --git a/src/main/java/org/writer/csv/CsvIgnore.java b/src/main/java/org/writer/csv/CsvIgnore.java new file mode 100644 index 0000000..2665c41 --- /dev/null +++ b/src/main/java/org/writer/csv/CsvIgnore.java @@ -0,0 +1,14 @@ +package org.writer.csv; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excludes a field from CSV serialization. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface CsvIgnore { +} diff --git a/src/main/java/org/writer/exception/CsvException.java b/src/main/java/org/writer/exception/CsvException.java new file mode 100644 index 0000000..070ff56 --- /dev/null +++ b/src/main/java/org/writer/exception/CsvException.java @@ -0,0 +1,7 @@ +package org.writer.exception; + +public class CsvException extends RuntimeException { + public CsvException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/writer/exception/CsvFieldAccessException.java b/src/main/java/org/writer/exception/CsvFieldAccessException.java new file mode 100644 index 0000000..98f8799 --- /dev/null +++ b/src/main/java/org/writer/exception/CsvFieldAccessException.java @@ -0,0 +1,7 @@ +package org.writer.exception; + +public class CsvFieldAccessException extends CsvException { + public CsvFieldAccessException(String fieldName, Throwable cause) { + super("Field access error: " + fieldName, cause); + } +} diff --git a/src/main/java/org/writer/exception/CsvWriteException.java b/src/main/java/org/writer/exception/CsvWriteException.java new file mode 100644 index 0000000..c308bb3 --- /dev/null +++ b/src/main/java/org/writer/exception/CsvWriteException.java @@ -0,0 +1,7 @@ +package org.writer.exception; + +public class CsvWriteException extends CsvException { + public CsvWriteException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/org/writer/Writable.java b/src/main/java/org/writer/writer/Writable.java similarity index 80% rename from src/main/java/org/writer/Writable.java rename to src/main/java/org/writer/writer/Writable.java index 9f60c45..0f87ba1 100644 --- a/src/main/java/org/writer/Writable.java +++ b/src/main/java/org/writer/writer/Writable.java @@ -1,4 +1,4 @@ -package org.writer; +package org.writer.writer; import java.util.List; diff --git a/src/main/java/org/writer/writer/csv/CsvWriter.java b/src/main/java/org/writer/writer/csv/CsvWriter.java new file mode 100644 index 0000000..0713681 --- /dev/null +++ b/src/main/java/org/writer/writer/csv/CsvWriter.java @@ -0,0 +1,215 @@ +package org.writer.writer.csv; + +import org.writer.csv.CsvColumn; +import org.writer.csv.CsvFile; +import org.writer.csv.CsvIgnore; +import org.writer.exception.CsvFieldAccessException; +import org.writer.exception.CsvWriteException; +import org.writer.writer.Writable; + +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.*; +import java.util.logging.Logger; +import java.util.stream.Collectors; + + +/** + * Utility class for writing annotated Java objects into CSV files. + *

Supports field-level configuration via {@link CsvColumn} and class-level + * configuration via {@link CsvFile}. Fields annotated with {@link CsvIgnore} + * are excluded from output.

+ * + *

Collections and maps are serialized using custom delimiters. Primitive + * types and enums are converted via {@link Object#toString()}.

+ * + *

Throws {@link org.writer.exception.CsvWriteException} if writing fails, + * and {@link org.writer.exception.CsvFieldAccessException} if field access + * is not possible.

+ */ +public class CsvWriter implements Writable { + + private static final Logger logger = Logger.getLogger(CsvWriter.class.getName()); + + /** + * Writes a list of objects into a CSV file. + *

+ * The header row is generated from field names or {@link CsvColumn#name()}. + * Each object is serialized into a row according to field order. + * + * @param data list of objects to serialize + * @param fileName target file path + * @throws CsvWriteException if writing fails + */ + @Override + public void writeToFile(List data, String fileName) { + if (!validateInput(data)) return; + + Class clazz = data.get(0).getClass(); + List fields = prepareFields(clazz); + + CsvFile fileAnnotation = clazz.getAnnotation(CsvFile.class); + String columnDelimiter = (fileAnnotation != null) ? fileAnnotation.columnDelimiter() : getDefaultColumnDelimiter(); + + try (FileWriter writer = new FileWriter(fileName)) { + writer.append(buildHeader(fields, columnDelimiter)).append('\n'); + + for (Object obj : data) { + writer.append(buildRow(obj, fields, columnDelimiter)).append('\n'); + } + logger.info("CSV file created successfully: " + fileName); + + } catch (IOException e) { + logger.severe("Error writing CSV file: " + e.getMessage()); + throw new CsvWriteException("Error writing CSV file: " + fileName, e); + } + } + + /** + * Validates input list before serialization. + * + * @param data list of objects + * @return true if list is non-null and non-empty + */ + private boolean validateInput(List data) { + if (data == null || data.isEmpty()) { + logger.warning("No data to write"); + return false; + } + return true; + } + + /** + * Collects fields of the given class for serialization. + *

+ * Excludes fields annotated with {@link CsvIgnore} and sorts them + * according to {@link CsvColumn#order()}. + * + * @param clazz object class + * @return sorted list of accessible fields + */ + private List prepareFields(Class clazz) { + return Arrays.stream(clazz.getDeclaredFields()) + .filter(f -> f.getAnnotation(CsvIgnore.class) == null) + .peek(f -> f.setAccessible(true)) + .sorted(Comparator.comparingInt(f -> { + CsvColumn column = f.getAnnotation(CsvColumn.class); + return column != null ? column.order() : Integer.MAX_VALUE; + })) + .collect(Collectors.toList()); + } + + /** + * Builds the CSV header row. + * + * @param fields fields to include + * @param columnDelimiter delimiter between columns + * @return header row string + */ + private String buildHeader(List fields, String columnDelimiter) { + return fields.stream() + .map(this::getColumnName) + .collect(Collectors.joining(columnDelimiter)); + } + + /** + * Builds a CSV row for a single object. + * + * @param obj object to serialize + * @param fields fields to include + * @param columnDelimiter delimiter between columns + * @return row string + */ + private String buildRow(Object obj, List fields, String columnDelimiter) { + return fields.stream() + .map(f -> getFieldValueAsString(obj, f)) + .collect(Collectors.joining(columnDelimiter)); + } + + /** + * Resolves column name for a field. + *

+ * Uses {@link CsvColumn#name()} if present, otherwise defaults to field name. + * + * @param field field to resolve + * @return column name + */ + private String getColumnName(Field field) { + CsvColumn column = field.getAnnotation(CsvColumn.class); + return (column != null && !column.name().isEmpty()) ? column.name() : field.getName(); + } + + /** + * Retrieves string value of a field from an object. + *

+ * Supports collections and maps with custom delimiters. + * + * @param obj object containing the field + * @param field field to access + * @return string representation of field value + * @throws CsvFieldAccessException if field access fails + */ + private String getFieldValueAsString(Object obj, Field field) { + try { + Object value = field.get(obj); + if (value instanceof Collection) { + CsvColumn column = field.getAnnotation(CsvColumn.class); + String delimiter = column != null ? column.collectionDelimiter() : getDefaultCollectionDelimiter(); + return serialize((Collection) value, delimiter); + } + + if (value instanceof Map) { + CsvColumn column = field.getAnnotation(CsvColumn.class); + String delimiter = column != null ? column.collectionDelimiter() : getDefaultCollectionDelimiter(); + return serialize((Map) value, delimiter); + } + return value != null ? value.toString() : ""; + } catch (IllegalAccessException e) { + logger.severe("Error access to field: " + field.getName()); + throw new CsvFieldAccessException(field.getName(), e); + } + } + + /** + * Returns default collection delimiter from {@link CsvColumn}. + */ + private static String getDefaultCollectionDelimiter() { + return (String) Arrays.stream(CsvColumn.class.getDeclaredMethods()) + .filter(m -> m.getName().equals("collectionDelimiter")) + .findFirst().get() // мы уверены, что метод существует, но, возможно, лучше воспользоваться orElseThrow() для самодостаточности метода + .getDefaultValue(); + } + + /** + * Returns default column delimiter from {@link CsvFile}. + */ + private static String getDefaultColumnDelimiter() { + return (String) Arrays.stream(CsvFile.class.getDeclaredMethods()) + .filter(m -> m.getName().equals("columnDelimiter")) + .findFirst().get() // мы уверены, что метод существует, но, возможно, лучше воспользоваться orElseThrow() для самодостаточности метода + .getDefaultValue(); + } + + /** + * Converts a {@link Collection} into a string joined by the given delimiter. + * Null elements are represented as empty strings. + */ + private String serialize(Collection collection, String delimiter) { + return collection.stream() + .map(e -> e == null ? "" : e.toString()) + .collect(Collectors.joining(delimiter)); + } + + /** + * Converts a {@link Map} into a string of key=value pairs joined by the given delimiter. + * Null keys or values are represented as empty strings. + */ + private String serialize(Map map, String delimiter) { + return map.entrySet().stream() + .map(e -> new StringBuilder().append(e.getKey() == null + ? "" + : e.getKey()).append("=").append(e.getValue() == null ? "" : e.getValue()).toString()) + .collect(Collectors.joining(delimiter)); + } +} diff --git a/src/test/java/org/writter/CsvWriterTest.java b/src/test/java/org/writter/CsvWriterTest.java new file mode 100644 index 0000000..9260850 --- /dev/null +++ b/src/test/java/org/writter/CsvWriterTest.java @@ -0,0 +1,65 @@ +package org.writter; + + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.writer.model.Months; +import org.writer.model.Person; +import org.writer.writer.csv.CsvWriter; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CsvWriterTest { + + @TempDir + Path tempDir; + + @Test + void testWriteValidData() throws IOException { + CsvWriter writer = new CsvWriter(); + List people = List.of( + Person.builder() + .firstName("Иван") + .lastName("Иванов") + .dayOfBirth(12) + .monthOfBirth(Months.JANUARY) + .yearOfBirth(1997) + .build(), + Person.builder() + .firstName("Мария") + .lastName("Петрова") + .dayOfBirth(5) + .monthOfBirth(Months.MARCH) + .yearOfBirth(1997) + .build() + ); + + Path file = tempDir.resolve("people.csv"); + writer.writeToFile(people, file.toString()); + + List lines = Files.readAllLines(file); + + assertEquals("firstName,lastName,dayOfBirth,monthOfBirth,yearOfBirth", lines.get(0)); + + assertEquals("Иван,Иванов,12,JANUARY,1997", lines.get(1)); + assertEquals("Мария,Петрова,5,MARCH,1997", lines.get(2)); + } + + @Test + void testWriteEmptyList() { + CsvWriter writer = new CsvWriter(); + assertDoesNotThrow(() -> writer.writeToFile(List.of(), "empty.csv")); + } + + @Test + void testWriteNullList() { + CsvWriter writer = new CsvWriter(); + assertDoesNotThrow(() -> writer.writeToFile(null, "null.csv")); + } +} From 90a02dfddc79a1a1fa2c266b35e810fc593452a9 Mon Sep 17 00:00:00 2001 From: EvgeniyM Date: Tue, 6 Jan 2026 04:14:53 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B5=D0=BA=D1=82=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B5=D0=B7=D0=B6=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D0=B8=D0=B7=20Maven=20=D0=B2=20Gradle=20=D0=B8=20=D0=BE=D1=84?= =?UTF-8?q?=D0=BE=D1=80=D0=BC=D0=BB=D0=B5=D0=BD=20=D0=B2=20=D0=B2=D0=B8?= =?UTF-8?q?=D0=B4=D0=B5=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B8=D0=BE=D1=82=D0=B5?= =?UTF-8?q?=D0=BA=D0=B8,=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B0=20?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BA=D0=B0=20=D0=B2=D1=85=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20Main,=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=B7=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 26 ++++++++++++++++++ pom.xml | 32 ---------------------- settings.gradle | 1 + src/main/java/org/writer/Main.java | 43 ------------------------------ 4 files changed, 27 insertions(+), 75 deletions(-) create mode 100644 build.gradle delete mode 100644 pom.xml create mode 100644 settings.gradle delete mode 100644 src/main/java/org/writer/Main.java diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..646daa5 --- /dev/null +++ b/build.gradle @@ -0,0 +1,26 @@ +plugins { + id 'java-library' +} + +group = 'org.writer' +version = '1.0-SNAPSHOT' + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +repositories { + mavenCentral() +} + +dependencies { + compileOnly 'org.projectlombok:lombok:1.18.34' + annotationProcessor 'org.projectlombok:lombok:1.18.34' + + testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/pom.xml b/pom.xml deleted file mode 100644 index effc1bd..0000000 --- a/pom.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - 4.0.0 - - org.writer - csv - 1.0-SNAPSHOT - - - 17 - 17 - UTF-8 - - - - - org.projectlombok - lombok - 1.18.34 - provided - - - org.junit.jupiter - junit-jupiter - 5.8.1 - test - - - - \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..30578a3 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'csv-writer-lib' \ No newline at end of file diff --git a/src/main/java/org/writer/Main.java b/src/main/java/org/writer/Main.java deleted file mode 100644 index 475b02a..0000000 --- a/src/main/java/org/writer/Main.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.writer; - -import org.writer.model.Months; -import org.writer.model.Person; -import org.writer.model.Student; -import org.writer.writer.Writable; -import org.writer.writer.csv.CsvWriter; - -import java.util.Arrays; -import java.util.List; - -public class Main { - public static void main(String[] args) { - Writable csvWriter = new CsvWriter(); - - List people = Arrays.asList( - Person.builder() - .firstName("Иван") - .lastName("Иванов") - .dayOfBirth(12) - .monthOfBirth(Months.JANUARY) - .yearOfBirth(1997).build(), - Person.builder() - .firstName("Мария") - .lastName("Петрова") - .dayOfBirth(5) - .monthOfBirth(Months.MARCH) - .yearOfBirth(1997).build() - ); - csvWriter.writeToFile(people, "persons.csv"); - - List students = Arrays.asList( - Student.builder() - .name("Алексей") - .score(Arrays.asList("5", "4", "3")).build(), - Student.builder() - .name("Ольга") - .score(Arrays.asList("4", "4", "5")).build() - ); - csvWriter.writeToFile(students, "students.csv"); - } -} - From 02c585beb3d7cfb99275a32c2d33a6edad715003 Mon Sep 17 00:00:00 2001 From: EvgeniyM Date: Sat, 17 Jan 2026 06:56:50 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=84=D0=B8=D0=B3?= =?UTF-8?q?=D1=83=D1=80=D0=B0=D1=86=D0=B8=D0=B8:=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=8C=D0=B7=D1=83=D0=B5=D0=BC=20toolchain=20=D0=B2?= =?UTF-8?q?=D0=BC=D0=B5=D1=81=D1=82=D0=BE=20=D1=83=D1=81=D1=82=D0=B0=D1=80?= =?UTF-8?q?=D0=B5=D0=B2=D1=88=D0=B8=D1=85=20=D0=BD=D0=B0=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=20=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 646daa5..63b3a9a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,8 +6,9 @@ group = 'org.writer' version = '1.0-SNAPSHOT' java { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } repositories {