From 1ea20323bdd467d440841bda5311cc5298213483 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 10 Nov 2025 22:53:25 +0300 Subject: [PATCH 1/2] input: DataProvider, FileDataProvider, RandomDataProvider (clean PR) --- .../team/shellsort/input/DataProvider.java | 18 ++++++ .../shellsort/input/FileDataProvider.java | 64 ++++++++++++++++++- .../shellsort/input/RandomDataProvider.java | 9 --- 3 files changed, 79 insertions(+), 12 deletions(-) create mode 100644 src/main/java/team/shellsort/input/DataProvider.java diff --git a/src/main/java/team/shellsort/input/DataProvider.java b/src/main/java/team/shellsort/input/DataProvider.java new file mode 100644 index 0000000..5b82096 --- /dev/null +++ b/src/main/java/team/shellsort/input/DataProvider.java @@ -0,0 +1,18 @@ +package team.shellsort.input; + +import java.io.IOException; + +/** + * Общий контракт для классов, загружающих данные о машинах + * (например, из файла или случайным образом). + */ +public interface DataProvider { + + /** + * Загружает данные о машинах. + * + * @return валидные объекты и исходные строки с ошибками + * @throws IOException для источников I/O (файл/сеть) + */ + LoadResult load() throws IOException; +} diff --git a/src/main/java/team/shellsort/input/FileDataProvider.java b/src/main/java/team/shellsort/input/FileDataProvider.java index a5dcf95..291d141 100644 --- a/src/main/java/team/shellsort/input/FileDataProvider.java +++ b/src/main/java/team/shellsort/input/FileDataProvider.java @@ -1,9 +1,67 @@ package team.shellsort.input; +import team.shellsort.model.Car; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * Источник данных, читающий записи о машинах из ресурса в classpath. + *

Формат строк: Модель;Мощность;Год (UTF-8). Пустые строки игнорируются.

+ */ public class FileDataProvider implements DataProvider { + + private static final String DEFAULT_RESOURCE = "CarList.txt"; + + private final Supplier readerSupplier; + + /** По умолчанию читаем из ресурса {@code CarList.txt}. */ + public FileDataProvider() { + this(DEFAULT_RESOURCE); + } + + /** Чтение из указанного ресурса classpath. */ + public FileDataProvider(String resourceName) { + this.readerSupplier = () -> { + InputStream is = getClass().getClassLoader().getResourceAsStream(resourceName); + if (is == null) throw new UncheckedIOException(new IOException("Resource not found: " + resourceName)); + return new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + }; + } + + /** Удобно для тестов: подаём внешний {@link Reader}. */ + public FileDataProvider(Reader reader) { + this.readerSupplier = () -> new BufferedReader(reader); + } + @Override - public LoadResult load(int limit) { - // TODO: реализовать чтение данных из файла - return null; + public LoadResult load() throws IOException { + List valid = new ArrayList<>(); + List invalid = new ArrayList<>(); + LineParser parser = new LineParser(); + + try (BufferedReader br = readerSupplier.get()) { + for (String line; (line = br.readLine()) != null; ) { + line = line.trim(); + if (line.isEmpty()) continue; + + Car car = parser.parse(line); // ожидаем: Модель;Мощность;Год + if (Validator.isValid(car)) valid.add(car); + else invalid.add(line); + } + } catch (UncheckedIOException e) { + // прокидываем исходный IOException + throw e.getCause(); + } + + return new LoadResult(valid, invalid); } } diff --git a/src/main/java/team/shellsort/input/RandomDataProvider.java b/src/main/java/team/shellsort/input/RandomDataProvider.java index 546a830..e69de29 100644 --- a/src/main/java/team/shellsort/input/RandomDataProvider.java +++ b/src/main/java/team/shellsort/input/RandomDataProvider.java @@ -1,9 +0,0 @@ -package team.shellsort.input; - -public class RandomDataProvider implements DataProvider { - @Override - public LoadResult load(int limit) { - // TODO: реализовать генерацию случайных данных - return null; - } -} From 84063a18548a26b91b465d6bbba577e6f82e0767 Mon Sep 17 00:00:00 2001 From: Mikhail Date: Mon, 10 Nov 2025 23:26:18 +0300 Subject: [PATCH 2/2] input: restore RandomDataProvider implementation --- .../shellsort/input/RandomDataProvider.java | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/src/main/java/team/shellsort/input/RandomDataProvider.java b/src/main/java/team/shellsort/input/RandomDataProvider.java index e69de29..64c9c91 100644 --- a/src/main/java/team/shellsort/input/RandomDataProvider.java +++ b/src/main/java/team/shellsort/input/RandomDataProvider.java @@ -0,0 +1,74 @@ +package team.shellsort.input; + +import team.shellsort.model.Car; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import java.util.random.RandomGenerator; + +/** + * Источник данных, генерирующий случайные записи о машинах. + * Используется для тестирования без внешних источников. + * Поддерживает детерминированную генерацию при передаче seed. + */ +public class RandomDataProvider implements DataProvider { + + /** Доступные значения для генерации. */ + private static final String[] MODELS = {"Almera", "Focus", "Lancer", "Camry", "Corolla"}; + private static final int[] YEARS = {2009, 2010, 2011, 2012, 2013, 2014, 2015}; + private static final int[] POWERS = {80, 90, 100, 110, 120, 125, 150, 180, 200, 250, 300}; + + /** Разделитель для сериализации «битых» строк. */ + private static final String SEP = ";"; + + /** Сколько записей генерировать. */ + private final int count; + + /** Генератор случайных чисел (может быть детерминированным при заданном seed). */ + private final RandomGenerator random; + + /** По умолчанию — 10 записей в недетерминированном режиме. */ + public RandomDataProvider() { + this(10, null); + } + + /** Конструктор с указанием количества записей. */ + public RandomDataProvider(int count) { + this(count, null); + } + + /** Конструктор с количеством записей и опциональным seed. */ + public RandomDataProvider(int count, Long seed) { + if (count < 0) throw new IllegalArgumentException("count must be >= 0"); + this.count = count; + this.random = (seed == null) ? ThreadLocalRandom.current() : new Random(seed); + } + + @Override + public LoadResult load() { + List valid = new ArrayList<>(); + List invalid = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + String model = MODELS[random.nextInt(MODELS.length)]; + int year = YEARS[random.nextInt(YEARS.length)]; + int power = POWERS[random.nextInt(POWERS.length)]; + + Car car = new Car.CarBuilder() + .setModel(model) + .setYear(year) + .setPower(power) + .build(); + + if (Validator.isValid(car)) { + valid.add(car); + } else { + invalid.add(model + SEP + power + SEP + year); + } + } + + return new LoadResult(valid, invalid); + } +}