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..64c9c91 100644
--- a/src/main/java/team/shellsort/input/RandomDataProvider.java
+++ b/src/main/java/team/shellsort/input/RandomDataProvider.java
@@ -1,9 +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(int limit) {
- // TODO: реализовать генерацию случайных данных
- return null;
+ 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);
}
}