-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathManualInputReaderStrategy.java
More file actions
270 lines (234 loc) · 11.1 KB
/
ManualInputReaderStrategy.java
File metadata and controls
270 lines (234 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
package input.strategy;
import dto.Client;
import input.CustomCollection;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
/**
* Стратегия для ручного ввода данных клиентов через консоль.
* Реализует интерфейс {@link ClientInputStrategy} для интерактивного
* ввода данных о клиентах с валидацией и подтверждением.
*
* <p>Обеспечивает:</p>
* <ul>
* <li>Пошаговый ввод данных о клиентах</li>
* <li>Валидацию введенных данных</li>
* <li>Подтверждение корректности ввода</li>
* <li>Ограничение на уникальность ID клиентов</li>
* <li>Лимит на максимальное количество клиентов (1000)</li>
* </ul>
*
* @see ClientInputStrategy
* @see Client
* @see CustomCollection
*/
public class ManualInputReaderStrategy implements ClientInputStrategy {
private Scanner scanner;
private Set<Integer> userIds;
public ManualInputReaderStrategy() {
this.scanner = new Scanner(System.in);
this.userIds = new HashSet<>();
}
public ManualInputReaderStrategy(InputStream in) {
this.scanner = new Scanner(in); // создаём Scanner на переданном потоке
this.userIds = new HashSet<>();
}
/**
* Получает коллекцию клиентов через интерактивный ручной ввод.
* Метод последовательно запрашивает данные о каждом клиенте,
* выполняет валидацию и подтверждение, после чего добавляет
* клиента в результирующую коллекцию.
*
* <p>Процесс ввода продолжается до тех пор, пока:</p>
* <ul>
* <li>Пользователь не откажется от добавления нового клиента</li>
* <li>Не будет достигнут лимит в 1000 клиентов</li>
* </ul>
*
* @return коллекция введенных клиентов
* @throws IOException если возникает ошибка ввода-вывода
*/
@Override
public CustomCollection<Client> getData() throws IOException {
CustomCollection<Client> clients = new CustomCollection<>();
System.out.println("=== Ручной ввод клиентов ===");
System.out.println("Вводите данные клиентов. Для завершения введите 'стоп'.\n");
int clientNumber = 1;
while (true) {
if (userIds.size() == 1000) {
System.out.println("Достигнут лимит клиентов");
break;
}
System.out.println("Ввод клиент №" + clientNumber);
try {
if (!promptForConfirmation("Добавляем клиента?")) {
break;
}
Client client = inputClient(clientNumber);
if (client != null) {
clients.add(client);
clientNumber++;
}
} catch (StopInputException e) {
System.out.println("\nВвод прерван пользователем. Текущий клиент не добавлен.");
break;
}
}
System.out.println("\n Ввод завершен. Добавлено клиентов: " + clients.size());
return clients;
}
/**
* Выполняет ввод данных для одного клиента.
* Запрашивает имя, телефонный номер и идентификатор,
* затем отображает введенные данные для подтверждения.
*
* @param clientNumber порядковый номер клиента (для отображения в интерфейсе)
* @return объект {@link Client} с введенными данными или {@code null},
* если пользователь отменил ввод
*/
private Client inputClient(int clientNumber) {
try {
String name = promptForName();
String phone = promptForNumber();
int id = promptForId();
Client.ClientBuilder builder = new Client.ClientBuilder().name(name)
.phoneNumber(phone)
.idNumber(id);
System.out.println("\nВы ввели:");
System.out.println(" Имя: " + name);
System.out.println(" Телефон: " + phone);
System.out.println(" ID: " + id);
if (promptForConfirmation("Все верно?")) {
return builder.build();
} else {
System.out.println("Отмена ввода этого клиента");
return null;
}
} catch (StopInputException e) {
throw e;
}
}
private String promptForName() {
while (true) {
try {
String name = promptForString("Введите имя", true);
if (name.matches("^([А-ЯЁ][а-яё]+\\s){1,2}[А-ЯЁ][а-яё]+$")) {
return name.trim().replaceAll("\\s+", " ");
} else {
System.out.println("Неверный формат! Используйте: Фамилия Имя или Фамилия Имя Отчество");
}
} catch (StopInputException e) {
throw e;
}
}
}
private String promptForNumber() {
while (true) {
try {
String phone = promptForString("Введите номер телефона в формате +7XXXXXXXXXX", true);
if (phone.matches("^\\+7\\d{10}$")) {
return phone;
} else {
System.out.println("⚠️ Неверный формат телефона! Пример: +79991234567");
System.out.println(" Должно начинаться с +7 и содержать 11 цифр");
}
} catch (StopInputException e) {
throw new RuntimeException(e);
}
}
}
private int promptForId() {
while (true) {
String input = promptForString("Введите ID в диапазоне от 0 до 1000", true);
try {
int id = Integer.parseInt(input);
if (id < 0 || id > 1000) {
System.out.println("ID должен быть в диапазоне от 0 до 1000");
continue;
}
if (userIds.contains(id)) {
System.out.println("Этот ID уже занят");
continue;
}
userIds.add(id);
return id;
} catch (StopInputException e) {
throw e;
} catch (NumberFormatException e) {
System.out.println("ID должен быть целым числом");
}
}
}
/**
* Запрашивает у пользователя подтверждение действия.
* Поддерживает ввод на русском и английском языках.
*
* <p>Принимаемые варианты подтверждения:</p>
* <ul>
* <li>Положительные: "да", "д", "y", "yes"</li>
* <li>Отрицательные: "нет", "н", "n", "no"</li>
* </ul>
*
* <p>Метод продолжает запрашивать ввод до получения корректного ответа.</p>
*
* @param message сообщение с вопросом для подтверждения
* @return {@code true} если пользователь подтвердил действие,
* {@code false} если отказался
*/
private boolean promptForConfirmation(String message) {
while (true) {
System.out.print(message + " (да/д/yes/y или нет/н/no/n): ");
String input = scanner.nextLine().trim().toLowerCase();
if (input.equalsIgnoreCase("стоп")) {
throw new StopInputException();
}
if (input.equals("да") || input.equals("д") || input.equals("y") || input.equals("yes")) {
return true;
}
if (input.equals("нет") || input.equals("н") || input.equals("n") || input.equals("no")) {
return false;
}
System.out.println("⚠️ Пожалуйста, введите 'да/д/yes/y' или 'нет/н/no/n'");
}
}
/**
* Универсальный метод для запроса строкового ввода от пользователя.
* Обрабатывает обязательность поля и базовые ошибки ввода.
*
* <p>Если поле является обязательным ({@code required = true}),
* метод будет продолжать запрашивать ввод до получения непустой строки.</p>
*
* @param message сообщение с описанием запрашиваемых данных
* @param required флаг, указывающий является ли поле обязательным для заполнения
* @return введенная пользователем строка (без начальных и конечных пробелов)
*/
private String promptForString(String message, boolean required) {
while (true) {
System.out.print(message + ": ");
try {
String input = scanner.nextLine().trim();
if (input.equalsIgnoreCase("стоп")) {
throw new StopInputException();
}
if (required && input.isEmpty()) {
System.out.println("⚠️ Это поле обязательно для заполнения!");
continue;
}
return input;
} catch (Exception e) {
if (e instanceof StopInputException) {
throw e; // Пробрасываем дальше
}
System.out.println("Ошибка ввода: " + e.getMessage());
scanner.nextLine(); // Очистка буфера
}
}
}
static class StopInputException extends RuntimeException {
public StopInputException() {
super("Ввод прерван пользователем");
}
}
}