diff --git a/app/src/main/java/ru/project/App.java b/app/src/main/java/ru/project/App.java new file mode 100644 index 0000000..2e2df6b --- /dev/null +++ b/app/src/main/java/ru/project/App.java @@ -0,0 +1,45 @@ +package ru.project; + +import com.googlecode.lanterna.gui2.MultiWindowTextGUI; +import com.googlecode.lanterna.screen.Screen; +import com.googlecode.lanterna.terminal.DefaultTerminalFactory; +import ru.project.model.AppState; +import ru.project.ui.MainMenu; + +public final class App { + + private App() {} + + public static void main(String[] args) { + Screen screen = null; + + try { + DefaultTerminalFactory terminalFactory = new DefaultTerminalFactory(); + + screen = terminalFactory.createScreen(); + screen.startScreen(); + + MultiWindowTextGUI gui = new MultiWindowTextGUI(screen); + + AppState state = new AppState(); + + MainMenu mainMenu = new MainMenu(gui, state); + + mainMenu.showModal(); + + } catch (Exception e) { + System.err.println("Произошла ошибка при выполнении программы:"); + e.printStackTrace(); + + } finally { + if (screen != null) { + try { + screen.stopScreen(); + } catch (Exception e) { + System.err.println("Не удалось корректно завершить работу терминала:"); + e.printStackTrace(); + } + } + } + } +} diff --git a/app/src/main/java/ru/project/model/AppState.java b/app/src/main/java/ru/project/model/AppState.java index 375877e..70f3beb 100644 --- a/app/src/main/java/ru/project/model/AppState.java +++ b/app/src/main/java/ru/project/model/AppState.java @@ -7,13 +7,9 @@ import ru.project.student.Student; public class AppState { private MyList mainCollection; - private MyList customDataCollection; - private String currentFilePath; public AppState() { this.mainCollection = new MyLinkedList<>(); - this.customDataCollection = new MyLinkedList<>(); - this.currentFilePath = null; } public MyList getMainCollection() { @@ -23,21 +19,4 @@ public class AppState { public void setMainCollection(MyList currentCollection) { this.mainCollection = currentCollection; } - - public MyList getCustomDataCollection() { - return customDataCollection; - } - - public void setCustomDataCollection(MyList customDataCollection) { - - this.customDataCollection = customDataCollection; - } - - public String getCurrentFilePath() { - return currentFilePath; - } - - public void setCurrentFilePath(String currentFilePath) { - this.currentFilePath = currentFilePath; - } } diff --git a/app/src/main/java/ru/project/random/RandomStudentGenerator.java b/app/src/main/java/ru/project/random/RandomStudentGenerator.java index 25e6032..526156a 100644 --- a/app/src/main/java/ru/project/random/RandomStudentGenerator.java +++ b/app/src/main/java/ru/project/random/RandomStudentGenerator.java @@ -1,156 +1,172 @@ package ru.project.random; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; + +import java.util.Random; +import java.util.regex.Pattern; import ru.project.collection.MyLinkedList; import ru.project.collection.MyList; import ru.project.student.Student; -import java.util.Random; -import java.util.regex.Pattern; +import ru.project.student.StudentBuilder; -public class RandomStudentGenerator -{ - private static final Random RANDOM = new Random(); - private String minGroupNumber; - private String maxGroupNumber; - private Double minAverageGrade; - private Double maxAverageGrade; - private Integer minRecordBookNumber; - private Integer maxRecordBookNumber; - private static final double DEFAULT_MIN_GRADE = 0.0; - private static final double DEFAULT_MAX_GRADE = 5.0; - private static final int DEFAULT_MIN_RECORD_BOOK = 1; - private static final String GROUP_PATTERN_REGEX = "^[A-Z][0-9]{2}$"; - private static final Pattern GROUP_PATTERN = Pattern.compile(GROUP_PATTERN_REGEX); - public RandomStudentGenerator setMinGroupNumber(String minGroupNumber) { - if (minGroupNumber != null && !GROUP_PATTERN.matcher(minGroupNumber).matches()) { - throw new IllegalArgumentException("minGroupNumber does not match Student group pattern: " + GROUP_PATTERN_REGEX); - } - this.minGroupNumber = minGroupNumber; - return this; - } - public RandomStudentGenerator setMaxGroupNumber(String maxGroupNumber) { - if (maxGroupNumber != null && !GROUP_PATTERN.matcher(maxGroupNumber).matches()) { - throw new IllegalArgumentException("maxGroupNumber does not match Student group pattern: " + GROUP_PATTERN_REGEX); - } - this.maxGroupNumber = maxGroupNumber; - return this; - } +public class RandomStudentGenerator { + private static final Random RANDOM = new Random(); + private String minGroupNumber; + private String maxGroupNumber; + private Double minAverageGrade; + private Double maxAverageGrade; + private Integer minRecordBookNumber; + private Integer maxRecordBookNumber; + private static final double DEFAULT_MIN_GRADE = 0.0; + private static final double DEFAULT_MAX_GRADE = 5.0; + private static final int DEFAULT_MIN_RECORD_BOOK = 1; + private static final String GROUP_PATTERN_REGEX = "^[A-Z][0-9]{2}$"; + private static final Pattern GROUP_PATTERN = Pattern.compile(GROUP_PATTERN_REGEX); - public RandomStudentGenerator setMinAverageGrade(double minAverageGrade) { - if (minAverageGrade < DEFAULT_MIN_GRADE || minAverageGrade > DEFAULT_MAX_GRADE) { - throw new IllegalArgumentException( - "minAverageGrade out of Student allowed range [" + DEFAULT_MIN_GRADE + ", " + DEFAULT_MAX_GRADE + "]" - ); - } - this.minAverageGrade = minAverageGrade; - return this; + public RandomStudentGenerator setMinGroupNumber(String minGroupNumber) { + if (minGroupNumber != null && !GROUP_PATTERN.matcher(minGroupNumber).matches()) { + throw new IllegalArgumentException( + "minGroupNumber does not match Student group pattern: " + GROUP_PATTERN_REGEX); } + this.minGroupNumber = minGroupNumber; + return this; + } - public RandomStudentGenerator setMaxAverageGrade(double maxAverageGrade) { - if (maxAverageGrade < DEFAULT_MIN_GRADE || maxAverageGrade > DEFAULT_MAX_GRADE) { - throw new IllegalArgumentException( - "maxAverageGrade out of Student allowed range [" + DEFAULT_MIN_GRADE + ", " + DEFAULT_MAX_GRADE + "]" - ); - } - this.maxAverageGrade = maxAverageGrade; - return this; + public RandomStudentGenerator setMaxGroupNumber(String maxGroupNumber) { + if (maxGroupNumber != null && !GROUP_PATTERN.matcher(maxGroupNumber).matches()) { + throw new IllegalArgumentException( + "maxGroupNumber does not match Student group pattern: " + GROUP_PATTERN_REGEX); } + this.maxGroupNumber = maxGroupNumber; + return this; + } - public RandomStudentGenerator setMinRecordBookNumber(int minRecordBookNumber) { - if (minRecordBookNumber < DEFAULT_MIN_RECORD_BOOK) { - throw new IllegalArgumentException( - "minRecordBookNumber must be >= " + DEFAULT_MIN_RECORD_BOOK - ); - } - this.minRecordBookNumber = minRecordBookNumber; - return this; + public RandomStudentGenerator setMinAverageGrade(double minAverageGrade) { + if (minAverageGrade < DEFAULT_MIN_GRADE || minAverageGrade > DEFAULT_MAX_GRADE) { + throw new IllegalArgumentException( + "minAverageGrade out of Student allowed range [" + + DEFAULT_MIN_GRADE + + ", " + + DEFAULT_MAX_GRADE + + "]"); } + this.minAverageGrade = minAverageGrade; + return this; + } - public RandomStudentGenerator setMaxRecordBookNumber(int maxRecordBookNumber) { - if (maxRecordBookNumber < DEFAULT_MIN_RECORD_BOOK) { - throw new IllegalArgumentException( - "maxRecordBookNumber must be >= " + DEFAULT_MIN_RECORD_BOOK - ); - } - this.maxRecordBookNumber = maxRecordBookNumber; - return this; + public RandomStudentGenerator setMaxAverageGrade(double maxAverageGrade) { + if (maxAverageGrade < DEFAULT_MIN_GRADE || maxAverageGrade > DEFAULT_MAX_GRADE) { + throw new IllegalArgumentException( + "maxAverageGrade out of Student allowed range [" + + DEFAULT_MIN_GRADE + + ", " + + DEFAULT_MAX_GRADE + + "]"); } - public MyList generate(int size) { - if (size < 0) { - throw new IllegalArgumentException("size must be non-negative"); - } - validateRanges(); - MyList result = new MyLinkedList<>(); - for (int i = 0; i < size; i++) { - String group = generateGroupNumber(); - double grade = generateAverageGrade(); - int recordBook = generateRecordBookNumber(); + this.maxAverageGrade = maxAverageGrade; + return this; + } - Student student = new StudentBuilder() - .setGroupNumber(group) - .setAverageGrade(grade) - .setRecordBookNumber(recordBook) - .build(); + public RandomStudentGenerator setMinRecordBookNumber(int minRecordBookNumber) { + if (minRecordBookNumber < DEFAULT_MIN_RECORD_BOOK) { + throw new IllegalArgumentException( + "minRecordBookNumber must be >= " + DEFAULT_MIN_RECORD_BOOK); + } + this.minRecordBookNumber = minRecordBookNumber; + return this; + } - result.add(student); - } - return result; + public RandomStudentGenerator setMaxRecordBookNumber(int maxRecordBookNumber) { + if (maxRecordBookNumber < DEFAULT_MIN_RECORD_BOOK) { + throw new IllegalArgumentException( + "maxRecordBookNumber must be >= " + DEFAULT_MIN_RECORD_BOOK); } - private void validateRanges() { + this.maxRecordBookNumber = maxRecordBookNumber; + return this; + } - double effectiveMinGrade = (minAverageGrade != null) ? minAverageGrade : DEFAULT_MIN_GRADE; - double effectiveMaxGrade = (maxAverageGrade != null) ? maxAverageGrade : DEFAULT_MAX_GRADE; - if (effectiveMinGrade > effectiveMaxGrade) { - throw new IllegalArgumentException("minAverageGrade cannot be greater than maxAverageGrade"); - } - int effectiveMinRecord = (minRecordBookNumber != null) ? minRecordBookNumber : DEFAULT_MIN_RECORD_BOOK; - int effectiveMaxRecord = (maxRecordBookNumber != null) ? maxRecordBookNumber : Integer.MAX_VALUE; - if (effectiveMaxRecord == Integer.MAX_VALUE) { - effectiveMaxRecord = effectiveMinRecord + 999_999; - } - if (effectiveMinRecord > effectiveMaxRecord) { - throw new IllegalArgumentException("minRecordBookNumber cannot be greater than maxRecordBookNumber"); - } - if (minGroupNumber != null && maxGroupNumber != null) { - if (minGroupNumber.compareTo(maxGroupNumber) > 0) { - throw new IllegalArgumentException("minGroupNumber cannot be greater than maxGroupNumber"); - } - } - } - private String generateGroupNumber() { - String min = (minGroupNumber != null) ? minGroupNumber : "A00"; - String max = (maxGroupNumber != null) ? maxGroupNumber : "Z99"; - char minLetter = min.charAt(0); - char maxLetter = max.charAt(0); - int minDigit = Integer.parseInt(min.substring(1)); - int maxDigit = Integer.parseInt(max.substring(1)); - char letter = (char) (minLetter + RANDOM.nextInt((maxLetter - minLetter) + 1)); - int digit; - if (letter == minLetter && letter == maxLetter) { - digit = minDigit + RANDOM.nextInt((maxDigit - minDigit) + 1); - } else if (letter == minLetter) { - digit = minDigit + RANDOM.nextInt(100 - minDigit); - } else if (letter == maxLetter) { - digit = RANDOM.nextInt(maxDigit + 1); - } else { - digit = RANDOM.nextInt(100); - } - return letter + String.format("%02d", digit); + public MyList generate(int size) { + if (size < 0) { + throw new IllegalArgumentException("size must be non-negative"); } + validateRanges(); + MyList result = new MyLinkedList<>(); + for (int i = 0; i < size; i++) { + String group = generateGroupNumber(); + double grade = generateAverageGrade(); + int recordBook = generateRecordBookNumber(); - private double generateAverageGrade() { - double min = (minAverageGrade != null) ? minAverageGrade : DEFAULT_MIN_GRADE; - double max = (maxAverageGrade != null) ? maxAverageGrade : DEFAULT_MAX_GRADE; + Student student = + new StudentBuilder() + .setGroupNumber(group) + .setAverageGrade(grade) + .setRecordBookNumber(recordBook) + .build(); - double range = max - min; - return min + (range * RANDOM.nextDouble()); + result.add(student); } - private int generateRecordBookNumber() { - int min = (minRecordBookNumber != null) ? minRecordBookNumber : DEFAULT_MIN_RECORD_BOOK; - int max = (maxRecordBookNumber != null) ? maxRecordBookNumber : min + 999_999; // разумный максимум - if (max < min) { - max = min; - } - return min + RANDOM.nextInt((max - min) + 1); + return result; + } + + private void validateRanges() { + + double effectiveMinGrade = (minAverageGrade != null) ? minAverageGrade : DEFAULT_MIN_GRADE; + double effectiveMaxGrade = (maxAverageGrade != null) ? maxAverageGrade : DEFAULT_MAX_GRADE; + if (effectiveMinGrade > effectiveMaxGrade) { + throw new IllegalArgumentException("minAverageGrade cannot be greater than maxAverageGrade"); } + int effectiveMinRecord = + (minRecordBookNumber != null) ? minRecordBookNumber : DEFAULT_MIN_RECORD_BOOK; + int effectiveMaxRecord = + (maxRecordBookNumber != null) ? maxRecordBookNumber : Integer.MAX_VALUE; + if (effectiveMaxRecord == Integer.MAX_VALUE) { + effectiveMaxRecord = effectiveMinRecord + 999_999; + } + if (effectiveMinRecord > effectiveMaxRecord) { + throw new IllegalArgumentException( + "minRecordBookNumber cannot be greater than maxRecordBookNumber"); + } + if (minGroupNumber != null && maxGroupNumber != null) { + if (minGroupNumber.compareTo(maxGroupNumber) > 0) { + throw new IllegalArgumentException("minGroupNumber cannot be greater than maxGroupNumber"); + } + } + } + + private String generateGroupNumber() { + String min = (minGroupNumber != null) ? minGroupNumber : "A00"; + String max = (maxGroupNumber != null) ? maxGroupNumber : "Z99"; + char minLetter = min.charAt(0); + char maxLetter = max.charAt(0); + int minDigit = Integer.parseInt(min.substring(1)); + int maxDigit = Integer.parseInt(max.substring(1)); + char letter = (char) (minLetter + RANDOM.nextInt((maxLetter - minLetter) + 1)); + int digit; + if (letter == minLetter && letter == maxLetter) { + digit = minDigit + RANDOM.nextInt((maxDigit - minDigit) + 1); + } else if (letter == minLetter) { + digit = minDigit + RANDOM.nextInt(100 - minDigit); + } else if (letter == maxLetter) { + digit = RANDOM.nextInt(maxDigit + 1); + } else { + digit = RANDOM.nextInt(100); + } + return letter + String.format("%02d", digit); + } + + private double generateAverageGrade() { + double min = (minAverageGrade != null) ? minAverageGrade : DEFAULT_MIN_GRADE; + double max = (maxAverageGrade != null) ? maxAverageGrade : DEFAULT_MAX_GRADE; + + double range = max - min; + return min + (range * RANDOM.nextDouble()); + } + + private int generateRecordBookNumber() { + int min = (minRecordBookNumber != null) ? minRecordBookNumber : DEFAULT_MIN_RECORD_BOOK; + int max = + (maxRecordBookNumber != null) ? maxRecordBookNumber : min + 999_999; // разумный максимум + if (max < min) { + max = min; + } + return min + RANDOM.nextInt((max - min) + 1); + } } diff --git a/app/src/main/java/ru/project/search/StudentSearchService.java b/app/src/main/java/ru/project/search/StudentSearchService.java index 0c7fe16..dc3a366 100644 --- a/app/src/main/java/ru/project/search/StudentSearchService.java +++ b/app/src/main/java/ru/project/search/StudentSearchService.java @@ -1,7 +1,7 @@ package ru.project.search; -import ru.project.collection.MyList; import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; import ru.project.student.Student; public class StudentSearchService { diff --git a/app/src/main/java/ru/project/sorting/EvenOnlyMergeSortStrategy.java b/app/src/main/java/ru/project/sorting/EvenOnlyMergeSortStrategy.java deleted file mode 100644 index 021a70e..0000000 --- a/app/src/main/java/ru/project/sorting/EvenOnlyMergeSortStrategy.java +++ /dev/null @@ -1,84 +0,0 @@ -package ru.project.sorting; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.function.ToIntFunction; -import ru.project.collection.MyList; - -public class EvenOnlyMergeSortStrategy implements SortStrategy { - - private final ToIntFunction valueExtractor; - - public EvenOnlyMergeSortStrategy(ToIntFunction valueExtractor) { - this.valueExtractor = valueExtractor; - } - - @Override - public void sort(MyList list, Comparator comparator) { - if (list == null || list.size() <= 1) { - return; - } - - List evenIndices = new ArrayList<>(); - List evenElements = new ArrayList<>(); - - for (int i = 0; i < list.size(); i++) { - T element = list.get(i); - int value = valueExtractor.applyAsInt(element); - if (value % 2 == 0) { - evenIndices.add(i); - evenElements.add(element); - } - } - - if (evenElements.size() <= 1) { - return; - } - - List sortedEven = mergeSort(evenElements, comparator); - - for (int j = 0; j < evenIndices.size(); j++) { - list.set(evenIndices.get(j), sortedEven.get(j)); - } - } - - private List mergeSort(List list, Comparator comparator) { - if (list.size() <= 1) { - return list; - } - - int mid = list.size() / 2; - List left = mergeSort(list.subList(0, mid), comparator); - List right = mergeSort(list.subList(mid, list.size()), comparator); - - return merge(left, right, comparator); - } - - private List merge(List left, List right, Comparator comparator) { - List result = new ArrayList<>(); - int i = 0, j = 0; - - while (i < left.size() && j < right.size()) { - if (comparator.compare(left.get(i), right.get(j)) <= 0) { - result.add(left.get(i)); - i++; - } else { - result.add(right.get(j)); - j++; - } - } - - while (i < left.size()) { - result.add(left.get(i)); - i++; - } - - while (j < right.size()) { - result.add(right.get(j)); - j++; - } - - return result; - } -} diff --git a/app/src/main/java/ru/project/ui/MainMenu.java b/app/src/main/java/ru/project/ui/MainMenu.java new file mode 100644 index 0000000..cf3c57f --- /dev/null +++ b/app/src/main/java/ru/project/ui/MainMenu.java @@ -0,0 +1,38 @@ +package ru.project.ui; + +import com.googlecode.lanterna.gui2.ActionListBox; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import ru.project.model.AppState; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.collection.CurrentCollectionMenu; +import ru.project.ui.creation.CreateCollectionMenu; + +public class MainMenu extends BaseModalWindow { + + private final ActionListBox menu; + + private final BaseModalWindow currentCollectionMenu; + private final BaseModalWindow createCollectionMenu; + + public MainMenu(WindowBasedTextGUI gui, AppState state) { + super("Главное меню", gui); + + this.currentCollectionMenu = new CurrentCollectionMenu(gui, state); + + this.createCollectionMenu = new CreateCollectionMenu(gui, state); + + menu = new ActionListBox(); + + menu.addItem("Меню текущей коллекции", currentCollectionMenu::showModal); + + menu.addItem("Меню создания новой коллекции", createCollectionMenu::showModal); + + menu.addItem("Завершить выполнение программы", this::exit); + + setComponent(menu); + } + + private void exit() { + close(); + } +} diff --git a/app/src/main/java/ru/project/ui/collection/CurrentCollectionMenu.java b/app/src/main/java/ru/project/ui/collection/CurrentCollectionMenu.java new file mode 100644 index 0000000..af294af --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/CurrentCollectionMenu.java @@ -0,0 +1,60 @@ +package ru.project.ui.collection; + +import com.googlecode.lanterna.gui2.ActionListBox; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import ru.project.model.AppState; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.collection.counting.CountOccurrencesMenu; +import ru.project.ui.collection.search.SearchCollectionMenu; +import ru.project.ui.collection.sorting.SortCollectionMenu; +import ru.project.ui.common.CollectionDisplayWindow; +import ru.project.ui.common.CollectionFileSaveWindow; + +public class CurrentCollectionMenu extends BaseModalWindow { + + private final AppState state; + + private final SortCollectionMenu sortCollectionMenu; + private final SearchCollectionMenu searchCollectionMenu; + private final CountOccurrencesMenu countOccurrencesMenu; + + public CurrentCollectionMenu(WindowBasedTextGUI gui, AppState state) { + + super("Меню текущей коллекции", gui); + + this.state = state; + + this.sortCollectionMenu = new SortCollectionMenu(gui, state); + + this.searchCollectionMenu = new SearchCollectionMenu(gui, state); + + this.countOccurrencesMenu = new CountOccurrencesMenu(gui, state); + + ActionListBox menu = new ActionListBox(); + menu.addItem("Показать коллекцию...", this::showCurrentCollection); + + menu.addItem("Сохранить коллекцию в файл...", this::saveCurrentCollection); + + menu.addItem("Меню сортировки коллекции", sortCollectionMenu::showModal); + + menu.addItem("Меню поиска элемента в коллекции", searchCollectionMenu::showModal); + + menu.addItem("Меню подсчёта количества вхождений элемента", countOccurrencesMenu::showModal); + + menu.addItem("Выйти", this::close); + + setComponent(menu); + } + + private void showCurrentCollection() { + CollectionDisplayWindow window = new CollectionDisplayWindow(gui, state.getMainCollection()); + + window.showModal(); + } + + private void saveCurrentCollection() { + CollectionFileSaveWindow window = new CollectionFileSaveWindow(gui, state.getMainCollection()); + + window.showModal(); + } +} diff --git a/app/src/main/java/ru/project/ui/collection/counting/CountOccurrencesMenu.java b/app/src/main/java/ru/project/ui/collection/counting/CountOccurrencesMenu.java new file mode 100644 index 0000000..26cd626 --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/counting/CountOccurrencesMenu.java @@ -0,0 +1,109 @@ +package ru.project.ui.collection.counting; + +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import ru.project.collection.MyList; +import ru.project.finder.StudentOccurrenceIndexFinder; +import ru.project.model.AppState; +import ru.project.student.Student; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.common.MessageWindow; + +public class CountOccurrencesMenu extends BaseModalWindow { + + private final AppState state; + + private final OccurrenceStudentSettingsWindow occurrenceStudentSettingsWindow; + + private Student selectedStudent; + private int occurrences; + + private final Label studentLabel; + private final Label occurrencesLabel; + + public CountOccurrencesMenu(WindowBasedTextGUI gui, AppState state) { + + super("Меню подсчёта количества вхождений элемента", gui); + + this.state = state; + this.selectedStudent = null; + this.occurrences = 0; + + this.studentLabel = new Label("Элемент для поиска: не задан"); + + this.occurrencesLabel = new Label("Количество вхождений: не подсчитано"); + + this.occurrenceStudentSettingsWindow = + new OccurrenceStudentSettingsWindow( + gui, + student -> { + selectedStudent = student; + + studentLabel.setText("Элемент для поиска: задан"); + }); + + Panel panel = new Panel(); + panel.setLayoutManager(new LinearLayout()); + + panel.addComponent(studentLabel); + panel.addComponent(occurrencesLabel); + + panel.addComponent(new Button("Настроить элемент для поиска...", this::setupStudent)); + + panel.addComponent(new Button("Выполнить подсчёт", this::countOccurrences)); + + panel.addComponent(new Button("Выйти", this::close)); + + setComponent(panel); + } + + private void setupStudent() { + occurrenceStudentSettingsWindow.showModal(); + } + + private void countOccurrences() { + MyList collection = state.getMainCollection(); + + if (collection == null || collection.isEmpty()) { + MessageWindow.showModal(gui, "Текущая коллекция пуста."); + + return; + } + + if (selectedStudent == null) { + MessageWindow.showModal(gui, "Сначала настройте элемент для поиска."); + + return; + } + + StudentOccurrenceIndexFinder finder = new StudentOccurrenceIndexFinder(); + + int[] occurrenceIndices = finder.findOccurrences(collection, selectedStudent); + + occurrences = occurrenceIndices.length; + + StringBuilder result = new StringBuilder(); + + result.append("Количество вхождений: ").append(occurrences); + + result.append("\nИндексы: "); + + if (occurrenceIndices.length == 0) { + result.append("нет"); + } else { + for (int i = 0; i < occurrenceIndices.length; i++) { + + if (i > 0) { + result.append(", "); + } + + result.append(occurrenceIndices[i]); + } + } + + occurrencesLabel.setText(result.toString()); + } +} diff --git a/app/src/main/java/ru/project/ui/collection/counting/OccurrenceStudentSettingsWindow.java b/app/src/main/java/ru/project/ui/collection/counting/OccurrenceStudentSettingsWindow.java new file mode 100644 index 0000000..06cd33d --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/counting/OccurrenceStudentSettingsWindow.java @@ -0,0 +1,172 @@ +package ru.project.ui.collection.counting; + +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import java.util.function.Consumer; +import ru.project.student.Student; +import ru.project.student.StudentBuilder; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.common.InputValueWindow; +import ru.project.ui.common.MessageWindow; + +public class OccurrenceStudentSettingsWindow extends BaseModalWindow { + + private final StudentBuilder studentBuilder; + private final Consumer onApply; + + private String groupNumber; + private Double averageGrade; + private Integer recordBookNumber; + + private final Label groupNumberLabel; + private final Label averageGradeLabel; + private final Label recordBookNumberLabel; + + public OccurrenceStudentSettingsWindow(WindowBasedTextGUI gui, Consumer onApply) { + + super("Настройка элемента для поиска", gui); + + this.studentBuilder = new StudentBuilder(); + this.onApply = onApply; + + Panel panel = new Panel(); + panel.setLayoutManager(new LinearLayout()); + + groupNumberLabel = new Label("Номер группы: не задан"); + + averageGradeLabel = new Label("Средний балл: не задан"); + + recordBookNumberLabel = new Label("Номер зачётной книжки: не задан"); + + panel.addComponent(groupNumberLabel); + panel.addComponent(averageGradeLabel); + panel.addComponent(recordBookNumberLabel); + + panel.addComponent(new Button("Задать номер группы...", this::setGroupNumber)); + + panel.addComponent(new Button("Задать средний балл...", this::setAverageGrade)); + + panel.addComponent(new Button("Задать номер зачётной книжки...", this::setRecordBookNumber)); + + panel.addComponent(new Button("Применить", this::apply)); + + panel.addComponent(new Button("Выйти", this::close)); + + setComponent(panel); + } + + private void setGroupNumber() { + InputValueWindow window = + new InputValueWindow( + gui, + "Номер группы", + "Введите номер группы (например, A12):", + groupNumber, + value -> { + if (!value.matches(Student.getGroupNumberPattern())) { + + MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12."); + + return false; + } + + groupNumber = value; + studentBuilder.setGroupNumber(value); + + groupNumberLabel.setText("Номер группы: " + groupNumber); + + return true; + }); + + window.showModal(); + } + + private void setAverageGrade() { + InputValueWindow window = + new InputValueWindow( + gui, + "Средний балл", + "Введите средний балл от 0.0 до 5.0:", + averageGrade == null ? null : String.valueOf(averageGrade), + value -> { + try { + double parsedValue = Double.parseDouble(value); + + if (parsedValue < Student.getMinAverageGrade() + || parsedValue > Student.getMaxAverageGrade()) { + + MessageWindow.showModal(gui, "Средний балл должен быть от 0.0 до 5.0."); + + return false; + } + + averageGrade = parsedValue; + studentBuilder.setAverageGrade(parsedValue); + + averageGradeLabel.setText("Средний балл: " + averageGrade); + + return true; + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите корректное число."); + + return false; + } + }); + + window.showModal(); + } + + private void setRecordBookNumber() { + InputValueWindow window = + new InputValueWindow( + gui, + "Номер зачётной книжки", + "Введите номер зачётной книжки:", + recordBookNumber == null ? null : String.valueOf(recordBookNumber), + value -> { + try { + int parsedValue = Integer.parseInt(value); + + if (parsedValue < Student.getMinRecordBookNumber()) { + + MessageWindow.showModal(gui, "Номер зачётной книжки должен быть положительным."); + + return false; + } + + recordBookNumber = parsedValue; + studentBuilder.setRecordBookNumber(parsedValue); + + recordBookNumberLabel.setText("Номер зачётной книжки: " + recordBookNumber); + + return true; + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите целое число."); + + return false; + } + }); + + window.showModal(); + } + + private void apply() { + if (groupNumber == null || averageGrade == null || recordBookNumber == null) { + + MessageWindow.showModal(gui, "Необходимо задать все поля элемента."); + + return; + } + + Student student = studentBuilder.build(); + + onApply.accept(student); + + MessageWindow.showModal(gui, "Элемент для поиска успешно настроен."); + } +} diff --git a/app/src/main/java/ru/project/ui/collection/search/FilterRelation.java b/app/src/main/java/ru/project/ui/collection/search/FilterRelation.java new file mode 100644 index 0000000..71c6cd2 --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/search/FilterRelation.java @@ -0,0 +1,6 @@ +package ru.project.ui.collection.search; + +public enum FilterRelation { + AND, + OR +} diff --git a/app/src/main/java/ru/project/ui/collection/search/SearchCollectionMenu.java b/app/src/main/java/ru/project/ui/collection/search/SearchCollectionMenu.java new file mode 100644 index 0000000..e820849 --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/search/SearchCollectionMenu.java @@ -0,0 +1,191 @@ +package ru.project.ui.collection.search; + +import com.googlecode.lanterna.gui2.ActionListBox; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.model.AppState; +import ru.project.search.AverageGradeRangeCriteria; +import ru.project.search.CompositeStudentSearchCriteria; +import ru.project.search.GroupNumberRangeCriteria; +import ru.project.search.RecordBookNumberRangeCriteria; +import ru.project.search.SearchOperation; +import ru.project.search.StudentSearchCriteria; +import ru.project.search.StudentSearchService; +import ru.project.student.Student; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.common.CollectionDisplayWindow; +import ru.project.ui.common.CollectionFileSaveWindow; +import ru.project.ui.common.MessageWindow; + +public class SearchCollectionMenu extends BaseModalWindow { + + private final AppState state; + + private final SearchSettingsWindow searchSettingsWindow; + + private SearchSettings searchSettings; + private MyList searchResultCollection; + + public SearchCollectionMenu(WindowBasedTextGUI gui, AppState state) { + + super("Меню поиска элемента в коллекции", gui); + + this.state = state; + this.searchSettings = new SearchSettings(); + this.searchResultCollection = new MyLinkedList<>(); + + this.searchSettingsWindow = + new SearchSettingsWindow(gui, settings -> this.searchSettings = settings); + + ActionListBox menu = new ActionListBox(); + + menu.addItem("Выполнить поиск", this::updateSearchResult); + + menu.addItem("Показать результат поиска", this::showSearchResult); + + menu.addItem("Сохранить результат поиска в файл", this::saveSearchResult); + + menu.addItem("Настроить процесс поиска...", searchSettingsWindow::showModal); + + menu.addItem("Выйти", this::close); + + setComponent(menu); + } + + private void updateSearchResult() { + MyList collection = state.getMainCollection(); + + if (collection == null || collection.isEmpty()) { + MessageWindow.showModal(gui, "Коллекция для поиска пуста."); + return; + } + + if (!hasEnabledFilters()) { + MessageWindow.showModal(gui, "Не выбран ни один фильтр."); + return; + } + + if (!hasValidFilterRanges()) { + return; + } + + StudentSearchCriteria criteria = createSearchCriteria(); + + StudentSearchService searchService = new StudentSearchService(criteria); + + searchResultCollection = searchService.find(collection); + + MessageWindow.showModal(gui, "Результат поиска обновлён."); + } + + private StudentSearchCriteria createSearchCriteria() { + SearchOperation operation = + searchSettings.getRelation() == FilterRelation.AND + ? SearchOperation.INTERSECTION + : SearchOperation.UNION; + + CompositeStudentSearchCriteria compositeCriteria = + new CompositeStudentSearchCriteria(operation); + + if (searchSettings.isGroupNumberFilterEnabled()) { + compositeCriteria.add( + new GroupNumberRangeCriteria( + searchSettings.getMinGroupNumber(), searchSettings.getMaxGroupNumber())); + } + + if (searchSettings.isAverageGradeFilterEnabled()) { + compositeCriteria.add( + new AverageGradeRangeCriteria( + searchSettings.getMinAverageGrade(), searchSettings.getMaxAverageGrade())); + } + + if (searchSettings.isRecordBookNumberFilterEnabled()) { + compositeCriteria.add( + new RecordBookNumberRangeCriteria( + searchSettings.getMinRecordBookNumber(), searchSettings.getMaxRecordBookNumber())); + } + + return compositeCriteria; + } + + private boolean hasEnabledFilters() { + return searchSettings.isGroupNumberFilterEnabled() + || searchSettings.isAverageGradeFilterEnabled() + || searchSettings.isRecordBookNumberFilterEnabled(); + } + + private boolean hasValidFilterRanges() { + StringBuilder errors = new StringBuilder(); + + if (!hasValidGroupNumberRange()) { + errors.append("Для фильтра по номеру группы задайте минимальное и максимальное значения.\n"); + } + + if (!hasValidAverageGradeRange()) { + errors.append("Для фильтра по среднему баллу задайте минимальное и максимальное значения.\n"); + } + + if (!hasValidRecordBookNumberRange()) { + errors.append( + "Для фильтра по номеру зачётной книжки задайте минимальное и максимальное значения.\n"); + } + + if (errors.length() > 0) { + MessageWindow.showModal(gui, errors.toString()); + return false; + } + + return true; + } + + private boolean hasValidGroupNumberRange() { + if (!searchSettings.isGroupNumberFilterEnabled()) { + return true; + } + + return searchSettings.getMinGroupNumber() != null && searchSettings.getMaxGroupNumber() != null; + } + + private boolean hasValidAverageGradeRange() { + if (!searchSettings.isAverageGradeFilterEnabled()) { + return true; + } + + return searchSettings.getMinAverageGrade() != null + && searchSettings.getMaxAverageGrade() != null; + } + + private boolean hasValidRecordBookNumberRange() { + if (!searchSettings.isRecordBookNumberFilterEnabled()) { + return true; + } + + return searchSettings.getMinRecordBookNumber() != null + && searchSettings.getMaxRecordBookNumber() != null; + } + + private void showSearchResult() { + if (searchResultCollection == null || searchResultCollection.isEmpty()) { + + MessageWindow.showModal(gui, "Результат поиска пуст."); + return; + } + + CollectionDisplayWindow window = new CollectionDisplayWindow(gui, searchResultCollection); + + window.showModal(); + } + + private void saveSearchResult() { + if (searchResultCollection == null || searchResultCollection.isEmpty()) { + + MessageWindow.showModal(gui, "Результат поиска пуст."); + return; + } + + CollectionFileSaveWindow window = new CollectionFileSaveWindow(gui, searchResultCollection); + + window.showModal(); + } +} diff --git a/app/src/main/java/ru/project/ui/collection/search/SearchSettings.java b/app/src/main/java/ru/project/ui/collection/search/SearchSettings.java new file mode 100644 index 0000000..058a59a --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/search/SearchSettings.java @@ -0,0 +1,116 @@ +package ru.project.ui.collection.search; + +public class SearchSettings { + + private FilterRelation relation; + + private boolean groupNumberFilterEnabled; + private boolean averageGradeFilterEnabled; + private boolean recordBookNumberFilterEnabled; + + private String minGroupNumber; + private String maxGroupNumber; + + private Double minAverageGrade; + private Double maxAverageGrade; + + private Integer minRecordBookNumber; + private Integer maxRecordBookNumber; + + public SearchSettings() { + this.relation = FilterRelation.AND; + + this.groupNumberFilterEnabled = false; + this.averageGradeFilterEnabled = false; + this.recordBookNumberFilterEnabled = false; + + this.minGroupNumber = null; + this.maxGroupNumber = null; + + this.minAverageGrade = null; + this.maxAverageGrade = null; + + this.minRecordBookNumber = null; + this.maxRecordBookNumber = null; + } + + public FilterRelation getRelation() { + return relation; + } + + public void setRelation(FilterRelation relation) { + this.relation = relation; + } + + public boolean isGroupNumberFilterEnabled() { + return groupNumberFilterEnabled; + } + + public void setGroupNumberFilterEnabled(boolean enabled) { + this.groupNumberFilterEnabled = enabled; + } + + public boolean isAverageGradeFilterEnabled() { + return averageGradeFilterEnabled; + } + + public void setAverageGradeFilterEnabled(boolean enabled) { + this.averageGradeFilterEnabled = enabled; + } + + public boolean isRecordBookNumberFilterEnabled() { + return recordBookNumberFilterEnabled; + } + + public void setRecordBookNumberFilterEnabled(boolean enabled) { + this.recordBookNumberFilterEnabled = enabled; + } + + public String getMinGroupNumber() { + return minGroupNumber; + } + + public void setMinGroupNumber(String minGroupNumber) { + this.minGroupNumber = minGroupNumber; + } + + public String getMaxGroupNumber() { + return maxGroupNumber; + } + + public void setMaxGroupNumber(String maxGroupNumber) { + this.maxGroupNumber = maxGroupNumber; + } + + public Double getMinAverageGrade() { + return minAverageGrade; + } + + public void setMinAverageGrade(Double minAverageGrade) { + this.minAverageGrade = minAverageGrade; + } + + public Double getMaxAverageGrade() { + return maxAverageGrade; + } + + public void setMaxAverageGrade(Double maxAverageGrade) { + this.maxAverageGrade = maxAverageGrade; + } + + public Integer getMinRecordBookNumber() { + return minRecordBookNumber; + } + + public void setMinRecordBookNumber(Integer minRecordBookNumber) { + this.minRecordBookNumber = minRecordBookNumber; + } + + public Integer getMaxRecordBookNumber() { + return maxRecordBookNumber; + } + + public void setMaxRecordBookNumber(Integer maxRecordBookNumber) { + this.maxRecordBookNumber = maxRecordBookNumber; + } +} diff --git a/app/src/main/java/ru/project/ui/collection/search/SearchSettingsWindow.java b/app/src/main/java/ru/project/ui/collection/search/SearchSettingsWindow.java new file mode 100644 index 0000000..059352c --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/search/SearchSettingsWindow.java @@ -0,0 +1,355 @@ +package ru.project.ui.collection.search; + +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.CheckBox; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.RadioBoxList; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import java.util.function.Consumer; +import java.util.function.Predicate; +import ru.project.student.Student; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.common.InputValueWindow; +import ru.project.ui.common.MessageWindow; + +public class SearchSettingsWindow extends BaseModalWindow { + + private final SearchSettings settings; + private final Consumer onApply; + + private final CheckBox groupNumberFilterCheckBox; + private final CheckBox averageGradeFilterCheckBox; + private final CheckBox recordBookNumberFilterCheckBox; + + private final Label minGroupNumberLabel; + private final Label maxGroupNumberLabel; + + private final Label minAverageGradeLabel; + private final Label maxAverageGradeLabel; + + private final Label minRecordBookNumberLabel; + private final Label maxRecordBookNumberLabel; + + private final RadioBoxList relationList; + + public SearchSettingsWindow(WindowBasedTextGUI gui, Consumer onApply) { + + super("Настройка процесса поиска", gui); + + this.settings = new SearchSettings(); + this.onApply = onApply; + + Panel panel = new Panel(); + panel.setLayoutManager(new LinearLayout()); + + panel.addComponent(new Label("Отношение фильтров:")); + + relationList = new RadioBoxList<>(); + relationList.addItem("И"); + relationList.addItem("ИЛИ"); + relationList.setCheckedItemIndex(0); + + panel.addComponent(relationList); + + groupNumberFilterCheckBox = new CheckBox("Фильтр по номеру группы"); + + averageGradeFilterCheckBox = new CheckBox("Фильтр по среднему баллу"); + + recordBookNumberFilterCheckBox = new CheckBox("Фильтр по номеру зачётной книжки"); + + panel.addComponent(groupNumberFilterCheckBox); + + minGroupNumberLabel = + new Label("Минимум номера группы: " + getValueOrDefault(settings.getMinGroupNumber())); + + maxGroupNumberLabel = + new Label("Максимум номера группы: " + getValueOrDefault(settings.getMaxGroupNumber())); + + panel.addComponent(minGroupNumberLabel); + panel.addComponent(maxGroupNumberLabel); + + panel.addComponent(new Button("Задать минимум номера группы...", this::setMinGroupNumber)); + + panel.addComponent(new Button("Задать максимум номера группы...", this::setMaxGroupNumber)); + + panel.addComponent(averageGradeFilterCheckBox); + + minAverageGradeLabel = + new Label("Минимум среднего балла: " + getValueOrDefault(settings.getMinAverageGrade())); + + maxAverageGradeLabel = + new Label("Максимум среднего балла: " + getValueOrDefault(settings.getMaxAverageGrade())); + + panel.addComponent(minAverageGradeLabel); + panel.addComponent(maxAverageGradeLabel); + + panel.addComponent(new Button("Задать минимум среднего балла...", this::setMinAverageGrade)); + + panel.addComponent(new Button("Задать максимум среднего балла...", this::setMaxAverageGrade)); + + panel.addComponent(recordBookNumberFilterCheckBox); + + minRecordBookNumberLabel = + new Label( + "Минимум номера зачётной книжки: " + + getValueOrDefault(settings.getMinRecordBookNumber())); + + maxRecordBookNumberLabel = + new Label( + "Максимум номера зачётной книжки: " + + getValueOrDefault(settings.getMaxRecordBookNumber())); + + panel.addComponent(minRecordBookNumberLabel); + panel.addComponent(maxRecordBookNumberLabel); + + panel.addComponent( + new Button("Задать минимум номера зачётной книжки...", this::setMinRecordBookNumber)); + + panel.addComponent( + new Button("Задать максимум номера зачётной книжки...", this::setMaxRecordBookNumber)); + + panel.addComponent(new Button("Применить", this::apply)); + + panel.addComponent(new Button("Выйти", this::close)); + + setComponent(panel); + } + + private void setMinGroupNumber() { + showGroupNumberInput( + "Минимальный номер группы", + settings.getMinGroupNumber(), + value -> { + if (!value.matches(Student.getGroupNumberPattern())) { + + MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12."); + + return false; + } + + if (settings.getMaxGroupNumber() != null + && value.compareTo(settings.getMaxGroupNumber()) > 0) { + + MessageWindow.showModal( + gui, "Минимальный номер группы не может быть больше максимального."); + + return false; + } + + settings.setMinGroupNumber(value); + + minGroupNumberLabel.setText("Минимум номера группы: " + value); + + return true; + }); + } + + private void setMaxGroupNumber() { + showGroupNumberInput( + "Максимальный номер группы", + settings.getMaxGroupNumber(), + value -> { + if (!value.matches(Student.getGroupNumberPattern())) { + + MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12."); + + return false; + } + + if (settings.getMinGroupNumber() != null + && value.compareTo(settings.getMinGroupNumber()) < 0) { + + MessageWindow.showModal( + gui, "Максимальный номер группы не может быть меньше минимального."); + + return false; + } + + settings.setMaxGroupNumber(value); + + maxGroupNumberLabel.setText("Максимум номера группы: " + value); + + return true; + }); + } + + private void showGroupNumberInput(String title, String initialValue, Predicate onSave) { + + InputValueWindow window = + new InputValueWindow( + gui, title, "Введите номер группы (например, A12):", initialValue, onSave); + + window.showModal(); + } + + private void setMinAverageGrade() { + showAverageGradeInput( + "Минимальный средний балл", + settings.getMinAverageGrade(), + value -> { + if (settings.getMaxAverageGrade() != null && value > settings.getMaxAverageGrade()) { + + MessageWindow.showModal( + gui, "Минимальный средний балл не может быть больше максимального."); + + return false; + } + + settings.setMinAverageGrade(value); + + minAverageGradeLabel.setText("Минимум среднего балла: " + value); + + return true; + }); + } + + private void setMaxAverageGrade() { + showAverageGradeInput( + "Максимальный средний балл", + settings.getMaxAverageGrade(), + value -> { + if (settings.getMinAverageGrade() != null && value < settings.getMinAverageGrade()) { + + MessageWindow.showModal( + gui, "Максимальный средний балл не может быть меньше минимального."); + + return false; + } + + settings.setMaxAverageGrade(value); + + maxAverageGradeLabel.setText("Максимум среднего балла: " + value); + + return true; + }); + } + + private void showAverageGradeInput(String title, Double initialValue, Predicate onSave) { + + InputValueWindow window = + new InputValueWindow( + gui, + title, + "Введите значение от 0.0 до 5.0:", + initialValue == null ? null : String.valueOf(initialValue), + value -> { + try { + double parsedValue = Double.parseDouble(value); + + if (parsedValue < Student.getMinAverageGrade() + || parsedValue > Student.getMaxAverageGrade()) { + + MessageWindow.showModal(gui, "Средний балл должен быть от 0.0 до 5.0."); + + return false; + } + + return onSave.test(parsedValue); + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите корректное число."); + + return false; + } + }); + + window.showModal(); + } + + private void setMinRecordBookNumber() { + showRecordBookNumberInput( + "Минимальный номер зачётной книжки", + settings.getMinRecordBookNumber(), + value -> { + if (settings.getMaxRecordBookNumber() != null + && value > settings.getMaxRecordBookNumber()) { + + MessageWindow.showModal( + gui, "Минимальный номер зачётной книжки не может быть больше максимального."); + + return false; + } + + settings.setMinRecordBookNumber(value); + + minRecordBookNumberLabel.setText("Минимум номера зачётной книжки: " + value); + + return true; + }); + } + + private void setMaxRecordBookNumber() { + showRecordBookNumberInput( + "Максимальный номер зачётной книжки", + settings.getMaxRecordBookNumber(), + value -> { + if (settings.getMinRecordBookNumber() != null + && value < settings.getMinRecordBookNumber()) { + + MessageWindow.showModal( + gui, "Максимальный номер зачётной книжки не может быть меньше минимального."); + + return false; + } + + settings.setMaxRecordBookNumber(value); + + maxRecordBookNumberLabel.setText("Максимум номера зачётной книжки: " + value); + + return true; + }); + } + + private void showRecordBookNumberInput( + String title, Integer initialValue, Predicate onSave) { + + InputValueWindow window = + new InputValueWindow( + gui, + title, + "Введите номер зачётной книжки:", + initialValue == null ? null : String.valueOf(initialValue), + value -> { + try { + int parsedValue = Integer.parseInt(value); + + if (parsedValue < Student.getMinRecordBookNumber()) { + + MessageWindow.showModal(gui, "Номер зачётной книжки должен быть положительным."); + + return false; + } + + return onSave.test(parsedValue); + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите целое число."); + + return false; + } + }); + + window.showModal(); + } + + private void apply() { + settings.setGroupNumberFilterEnabled(groupNumberFilterCheckBox.isChecked()); + + settings.setAverageGradeFilterEnabled(averageGradeFilterCheckBox.isChecked()); + + settings.setRecordBookNumberFilterEnabled(recordBookNumberFilterCheckBox.isChecked()); + + settings.setRelation( + relationList.getCheckedItemIndex() == 0 ? FilterRelation.AND : FilterRelation.OR); + + onApply.accept(settings); + + MessageWindow.showModal(gui, "Настройки процесса поиска успешно применены."); + } + + private String getValueOrDefault(Object value) { + return value == null ? "не задан" : String.valueOf(value); + } +} diff --git a/app/src/main/java/ru/project/ui/sorting/NormalSortSettingsWindow.java b/app/src/main/java/ru/project/ui/collection/sorting/NormalSortSettingsWindow.java similarity index 97% rename from app/src/main/java/ru/project/ui/sorting/NormalSortSettingsWindow.java rename to app/src/main/java/ru/project/ui/collection/sorting/NormalSortSettingsWindow.java index 0f3bbd2..6e66e45 100644 --- a/app/src/main/java/ru/project/ui/sorting/NormalSortSettingsWindow.java +++ b/app/src/main/java/ru/project/ui/collection/sorting/NormalSortSettingsWindow.java @@ -1,4 +1,4 @@ -package ru.project.ui.sorting; +package ru.project.ui.collection.sorting; import com.googlecode.lanterna.gui2.ActionListBox; import com.googlecode.lanterna.gui2.Button; diff --git a/app/src/main/java/ru/project/ui/sorting/ParitySortSettingsWindow.java b/app/src/main/java/ru/project/ui/collection/sorting/ParitySortSettingsWindow.java similarity index 94% rename from app/src/main/java/ru/project/ui/sorting/ParitySortSettingsWindow.java rename to app/src/main/java/ru/project/ui/collection/sorting/ParitySortSettingsWindow.java index aa6d090..b458b43 100644 --- a/app/src/main/java/ru/project/ui/sorting/ParitySortSettingsWindow.java +++ b/app/src/main/java/ru/project/ui/collection/sorting/ParitySortSettingsWindow.java @@ -1,4 +1,4 @@ -package ru.project.ui.sorting; +package ru.project.ui.collection.sorting; import com.googlecode.lanterna.gui2.Button; import com.googlecode.lanterna.gui2.Label; diff --git a/app/src/main/java/ru/project/ui/collection/sorting/SortAlgorithm.java b/app/src/main/java/ru/project/ui/collection/sorting/SortAlgorithm.java new file mode 100644 index 0000000..4f193a6 --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/sorting/SortAlgorithm.java @@ -0,0 +1,6 @@ +package ru.project.ui.collection.sorting; + +public enum SortAlgorithm { + BUBBLE, + MERGE +} diff --git a/app/src/main/java/ru/project/ui/sorting/SortCollectionMenu.java b/app/src/main/java/ru/project/ui/collection/sorting/SortCollectionMenu.java similarity index 50% rename from app/src/main/java/ru/project/ui/sorting/SortCollectionMenu.java rename to app/src/main/java/ru/project/ui/collection/sorting/SortCollectionMenu.java index ae72b4b..fc8c774 100644 --- a/app/src/main/java/ru/project/ui/sorting/SortCollectionMenu.java +++ b/app/src/main/java/ru/project/ui/collection/sorting/SortCollectionMenu.java @@ -1,11 +1,20 @@ -package ru.project.ui.sorting; +package ru.project.ui.collection.sorting; import com.googlecode.lanterna.gui2.Button; import com.googlecode.lanterna.gui2.Label; import com.googlecode.lanterna.gui2.LinearLayout; import com.googlecode.lanterna.gui2.Panel; import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import java.util.Comparator; import ru.project.model.AppState; +import ru.project.sorting.BubbleSortStrategy; +import ru.project.sorting.EvenOnlySortStrategy; +import ru.project.sorting.MergeSortStrategy; +import ru.project.sorting.SortStrategy; +import ru.project.sorting.StudentAverageGradeComparator; +import ru.project.sorting.StudentGroupComparator; +import ru.project.sorting.StudentRecordBookComparator; +import ru.project.student.Student; import ru.project.ui.base.BaseModalWindow; import ru.project.ui.common.MessageWindow; @@ -13,30 +22,39 @@ public class SortCollectionMenu extends BaseModalWindow { private final AppState state; - private final Label sortMethodLabel; + private final Label sortAlgorithmLabel; private final Label sortFieldLabel; + private final Label sortMethodLabel; - private final SortMethodSelectionWindow sortMethodSelectionWindow; private final NormalSortSettingsWindow normalSortSettingsWindow; private final ParitySortSettingsWindow paritySortSettingsWindow; + private final SortMethodSelectionWindow sortMethodSelectionWindow; + private final SortStrategySelectionWindow sortStrategySelectionWindow; - private SortMethod selectedSortMethod; + private SortAlgorithm selectedSortAlgorithm; private SortField selectedSortField; + private SortMethod selectedSortMethod; public SortCollectionMenu(WindowBasedTextGUI gui, AppState state) { super("Меню сортировки текущей коллекции", gui); this.state = state; - this.selectedSortMethod = SortMethod.NORMAL; + + this.selectedSortAlgorithm = SortAlgorithm.BUBBLE; this.selectedSortField = SortField.GROUP_NUMBER; + this.selectedSortMethod = SortMethod.NORMAL; Panel panel = new Panel(); panel.setLayoutManager(new LinearLayout()); this.sortMethodLabel = new Label("Способ сортировки: " + getSortMethodName(selectedSortMethod)); - this.sortFieldLabel = new Label("Сортировать по: " + getSortFieldName(selectedSortField)); + this.sortAlgorithmLabel = + new Label("Алгоритм сортировки: " + getSortAlgorithmName(selectedSortAlgorithm)); + + this.sortFieldLabel = + new Label("Сортировать по: " + getSortFieldName(selectedSortMethod, selectedSortField)); this.sortMethodSelectionWindow = new SortMethodSelectionWindow( @@ -46,6 +64,19 @@ public class SortCollectionMenu extends BaseModalWindow { sortMethodLabel.setText( "Способ сортировки: " + getSortMethodName(selectedSortMethod)); + + sortFieldLabel.setText( + "Сортировать по: " + getSortFieldName(selectedSortMethod, selectedSortField)); + }); + + this.sortStrategySelectionWindow = + new SortStrategySelectionWindow( + gui, + sortAlgorithm -> { + selectedSortAlgorithm = sortAlgorithm; + + sortAlgorithmLabel.setText( + "Алгоритм сортировки: " + getSortAlgorithmName(selectedSortAlgorithm)); }); this.normalSortSettingsWindow = @@ -53,17 +84,23 @@ public class SortCollectionMenu extends BaseModalWindow { gui, sortField -> { selectedSortField = sortField; + + sortFieldLabel.setText( + "Сортировать по: " + getSortFieldName(selectedSortMethod, selectedSortField)); }); this.paritySortSettingsWindow = new ParitySortSettingsWindow(gui); panel.addComponent(sortMethodLabel); panel.addComponent(sortFieldLabel); + panel.addComponent(sortAlgorithmLabel); - panel.addComponent(new Button("Выбрать способ сортировки", this::selectSortMethod)); + panel.addComponent(new Button("Выбрать способ сортировки...", this::selectSortMethod)); + + panel.addComponent(new Button("Выбрать алгоритм сортировки...", this::selectSortAlgorithm)); panel.addComponent( - new Button("Настроить текущий способ сортировки", this::setupCurrentSortMethod)); + new Button("Настроить текущий способ сортировки...", this::setupCurrentSortMethod)); panel.addComponent(new Button("Сортировать текущую коллекцию", this::sortCurrentCollection)); @@ -84,6 +121,10 @@ public class SortCollectionMenu extends BaseModalWindow { sortMethodSelectionWindow.showModal(); } + private void selectSortAlgorithm() { + sortStrategySelectionWindow.showModal(); + } + private void sortCurrentCollection() { if (state.getMainCollection() == null || state.getMainCollection().isEmpty()) { @@ -100,26 +141,74 @@ public class SortCollectionMenu extends BaseModalWindow { } private void sortNormally() { - // TODO - MessageWindow.showModal(gui, "Обычная сортировка не реализована."); + SortStrategy sortStrategy = getSortStrategy(); + + Comparator comparator = getSortComparator(); + + sortStrategy.sort(state.getMainCollection(), comparator); + + MessageWindow.showModal(gui, "Обычная сортировка успешно выполнена."); } private void sortByParity() { - // TODO - MessageWindow.showModal(gui, "Сортировка по чётности не реализована."); + SortStrategy sortStrategy = + new EvenOnlySortStrategy<>(getSortStrategy(), Student::getRecordBookNumber); + + Comparator comparator = new StudentRecordBookComparator(); + + sortStrategy.sort(state.getMainCollection(), comparator); + + MessageWindow.showModal(gui, "Сортировка по чётности успешно выполнена."); + } + + private SortStrategy getSortStrategy() { + return switch (selectedSortAlgorithm) { + case BUBBLE -> new BubbleSortStrategy<>(); + + case MERGE -> new MergeSortStrategy<>(); + }; + } + + private Comparator getSortComparator() { + return switch (selectedSortField) { + case GROUP_NUMBER -> new StudentGroupComparator(); + + case AVERAGE_GRADE -> new StudentAverageGradeComparator(); + + case RECORD_BOOK_NUMBER -> new StudentRecordBookComparator(); + }; } private String getSortMethodName(SortMethod sortMethod) { return switch (sortMethod) { case NORMAL -> "Обычная сортировка"; + case PARITY -> "Сортировка по чётности"; }; } - private String getSortFieldName(SortField sortField) { + private String getSortAlgorithmName(SortAlgorithm sortAlgorithm) { + return switch (sortAlgorithm) { + case BUBBLE -> "Сортировка пузырьком"; + + case MERGE -> "Сортировка слиянием"; + }; + } + + private String getSortFieldName(SortMethod sortMethod, SortField sortField) { + return switch (sortMethod) { + case NORMAL -> getNormalSortFieldName(sortField); + + case PARITY -> "номеру зачётной книжки"; + }; + } + + private String getNormalSortFieldName(SortField sortField) { return switch (sortField) { case GROUP_NUMBER -> "номеру группы"; + case AVERAGE_GRADE -> "среднему баллу"; + case RECORD_BOOK_NUMBER -> "номеру зачётной книжки"; }; } diff --git a/app/src/main/java/ru/project/ui/sorting/SortField.java b/app/src/main/java/ru/project/ui/collection/sorting/SortField.java similarity index 65% rename from app/src/main/java/ru/project/ui/sorting/SortField.java rename to app/src/main/java/ru/project/ui/collection/sorting/SortField.java index 6219f9b..5b6124b 100644 --- a/app/src/main/java/ru/project/ui/sorting/SortField.java +++ b/app/src/main/java/ru/project/ui/collection/sorting/SortField.java @@ -1,4 +1,4 @@ -package ru.project.ui.sorting; +package ru.project.ui.collection.sorting; public enum SortField { GROUP_NUMBER, diff --git a/app/src/main/java/ru/project/ui/sorting/SortMethod.java b/app/src/main/java/ru/project/ui/collection/sorting/SortMethod.java similarity index 52% rename from app/src/main/java/ru/project/ui/sorting/SortMethod.java rename to app/src/main/java/ru/project/ui/collection/sorting/SortMethod.java index 446f27e..9e56a8d 100644 --- a/app/src/main/java/ru/project/ui/sorting/SortMethod.java +++ b/app/src/main/java/ru/project/ui/collection/sorting/SortMethod.java @@ -1,4 +1,4 @@ -package ru.project.ui.sorting; +package ru.project.ui.collection.sorting; public enum SortMethod { NORMAL, diff --git a/app/src/main/java/ru/project/ui/sorting/SortMethodSelectionWindow.java b/app/src/main/java/ru/project/ui/collection/sorting/SortMethodSelectionWindow.java similarity index 95% rename from app/src/main/java/ru/project/ui/sorting/SortMethodSelectionWindow.java rename to app/src/main/java/ru/project/ui/collection/sorting/SortMethodSelectionWindow.java index 121e5e2..dc49e68 100644 --- a/app/src/main/java/ru/project/ui/sorting/SortMethodSelectionWindow.java +++ b/app/src/main/java/ru/project/ui/collection/sorting/SortMethodSelectionWindow.java @@ -1,4 +1,4 @@ -package ru.project.ui.sorting; +package ru.project.ui.collection.sorting; import com.googlecode.lanterna.gui2.ActionListBox; import com.googlecode.lanterna.gui2.WindowBasedTextGUI; diff --git a/app/src/main/java/ru/project/ui/collection/sorting/SortStrategySelectionWindow.java b/app/src/main/java/ru/project/ui/collection/sorting/SortStrategySelectionWindow.java new file mode 100644 index 0000000..2084e4e --- /dev/null +++ b/app/src/main/java/ru/project/ui/collection/sorting/SortStrategySelectionWindow.java @@ -0,0 +1,34 @@ +package ru.project.ui.collection.sorting; + +import com.googlecode.lanterna.gui2.ActionListBox; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import java.util.function.Consumer; +import ru.project.ui.base.BaseModalWindow; + +public class SortStrategySelectionWindow extends BaseModalWindow { + + private final Consumer onSortAlgorithmSelected; + + public SortStrategySelectionWindow( + WindowBasedTextGUI gui, Consumer onSortAlgorithmSelected) { + + super("Выбор алгоритма сортировки", gui); + + this.onSortAlgorithmSelected = onSortAlgorithmSelected; + + ActionListBox menu = new ActionListBox(); + + menu.addItem("Сортировка пузырьком", () -> selectSortAlgorithm(SortAlgorithm.BUBBLE)); + + menu.addItem("Сортировка слиянием", () -> selectSortAlgorithm(SortAlgorithm.MERGE)); + + menu.addItem("Выйти", this::close); + + setComponent(menu); + } + + private void selectSortAlgorithm(SortAlgorithm sortAlgorithm) { + onSortAlgorithmSelected.accept(sortAlgorithm); + close(); + } +} diff --git a/app/src/main/java/ru/project/ui/common/CollectionFileSaveWindow.java b/app/src/main/java/ru/project/ui/common/CollectionFileSaveWindow.java new file mode 100644 index 0000000..a8ce5eb --- /dev/null +++ b/app/src/main/java/ru/project/ui/common/CollectionFileSaveWindow.java @@ -0,0 +1,86 @@ +package ru.project.ui.common; + +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import com.googlecode.lanterna.gui2.dialogs.FileDialogBuilder; +import com.googlecode.lanterna.gui2.dialogs.MessageDialog; +import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton; +import java.io.File; +import java.io.IOException; +import ru.project.collection.MyList; +import ru.project.io.StudentCsvWriter; +import ru.project.student.Student; + +public class CollectionFileSaveWindow { + + private final WindowBasedTextGUI gui; + private final MyList collection; + + public CollectionFileSaveWindow(WindowBasedTextGUI gui, MyList collection) { + + this.gui = gui; + this.collection = collection; + } + + public void showModal() { + File selectedFile = selectFile(); + + if (selectedFile == null) { + return; + } + + if (selectedFile.exists()) { + saveToFile(selectedFile); + return; + } + + MessageDialogButton result = + MessageDialog.showMessageDialog( + gui, + "Создание файла", + "Файл не существует. Создать новый файл?", + MessageDialogButton.Yes, + MessageDialogButton.No); + + if (result == MessageDialogButton.Yes) { + createAndSaveToFile(selectedFile); + } + } + + private File selectFile() { + return new FileDialogBuilder() + .setTitle("Сохранение коллекции") + .setDescription("Выберите файл:") + .setActionLabel("Сохранить") + .build() + .showDialog(gui); + } + + private void createAndSaveToFile(File file) { + try { + if (!file.createNewFile()) { + MessageWindow.showModal(gui, "Не удалось создать файл."); + return; + } + + saveToFile(file); + + } catch (IOException e) { + MessageWindow.showModal(gui, "Не удалось создать файл."); + } + } + + private void saveToFile(File file) { + try { + StudentCsvWriter writer = new StudentCsvWriter(file.getAbsolutePath()); + + for (int i = 0; i < collection.size(); i++) { + writer.write(collection.get(i)); + } + + MessageWindow.showModal(gui, "Коллекция успешно сохранена."); + + } catch (IOException e) { + MessageWindow.showModal(gui, "Не удалось сохранить коллекцию."); + } + } +} diff --git a/app/src/main/java/ru/project/ui/common/CollectionSizeInputWindow.java b/app/src/main/java/ru/project/ui/common/CollectionSizeInputWindow.java new file mode 100644 index 0000000..57b1d3a --- /dev/null +++ b/app/src/main/java/ru/project/ui/common/CollectionSizeInputWindow.java @@ -0,0 +1,51 @@ +package ru.project.ui.common; + +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import java.util.function.Consumer; + +public class CollectionSizeInputWindow { + + private final WindowBasedTextGUI gui; + private final Integer currentSize; + private final Consumer onSizeEntered; + + public CollectionSizeInputWindow( + WindowBasedTextGUI gui, Integer currentSize, Consumer onSizeEntered) { + + this.gui = gui; + this.currentSize = currentSize; + this.onSizeEntered = onSizeEntered; + } + + public void showModal() { + InputValueWindow window = + new InputValueWindow( + gui, + "Размер коллекции", + "Введите размер коллекции:", + currentSize != null && currentSize > 0 ? String.valueOf(currentSize) : null, + this::handleSizeEntered); + + window.showModal(); + } + + private boolean handleSizeEntered(String value) { + try { + int size = Integer.parseInt(value); + + if (size <= 0) { + MessageWindow.showModal(gui, "Размер должен быть больше 0."); + + return false; + } + + onSizeEntered.accept(size); + return true; + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите целое число."); + + return false; + } + } +} diff --git a/app/src/main/java/ru/project/ui/creation/CreateCollectionMenu.java b/app/src/main/java/ru/project/ui/creation/CreateCollectionMenu.java index 72820be..76043ea 100644 --- a/app/src/main/java/ru/project/ui/creation/CreateCollectionMenu.java +++ b/app/src/main/java/ru/project/ui/creation/CreateCollectionMenu.java @@ -5,97 +5,82 @@ import com.googlecode.lanterna.gui2.Label; import com.googlecode.lanterna.gui2.LinearLayout; import com.googlecode.lanterna.gui2.Panel; import com.googlecode.lanterna.gui2.WindowBasedTextGUI; -import ru.project.collection.MyLinkedList; -import ru.project.collection.MyList; import ru.project.model.AppState; -import ru.project.student.Student; import ru.project.ui.base.BaseModalWindow; -import ru.project.ui.common.InputValueWindow; import ru.project.ui.common.MessageWindow; +import ru.project.ui.creation.file.FileDataSettingsWindow; +import ru.project.ui.creation.file.FileFillSettings; +import ru.project.ui.creation.file.FileFillStrategy; +import ru.project.ui.creation.manual.ManualDataSettingsWindow; +import ru.project.ui.creation.manual.ManualFillSettings; +import ru.project.ui.creation.manual.ManualFillStrategy; +import ru.project.ui.creation.random.RandomDataSettingsWindow; +import ru.project.ui.creation.random.RandomFillSettings; +import ru.project.ui.creation.random.RandomFillStrategy; public class CreateCollectionMenu extends BaseModalWindow { - private int collectionSize; - private FillMethod selectedFillMethod; + private final AppState state; + + private FillMethod selectedFillMethod; + private FillStrategy fillStrategy; - private final Label sizeLabel; private final Label fillMethodLabel; - private final CustomDataSettingsWindow customDataSettingsWindow; + private final ManualDataSettingsWindow manualDataSettingsWindow; + private final FileDataSettingsWindow fileDataSettingsWindow; + private final RandomDataSettingsWindow randomDataSettingsWindow; public CreateCollectionMenu(WindowBasedTextGUI gui, AppState state) { + super("Создание новой коллекции", gui); - collectionSize = 0; - selectedFillMethod = FillMethod.CUSTOM; + this.state = state; + this.selectedFillMethod = FillMethod.CUSTOM; + this.fillStrategy = null; - customDataSettingsWindow = new CustomDataSettingsWindow(gui, state, () -> collectionSize); + this.manualDataSettingsWindow = new ManualDataSettingsWindow(gui, this::setManualFillStrategy); + this.fileDataSettingsWindow = new FileDataSettingsWindow(gui, this::setFileFillStrategy); + this.randomDataSettingsWindow = new RandomDataSettingsWindow(gui, this::setRandomFillStrategy); Panel panel = new Panel(); panel.setLayoutManager(new LinearLayout()); - sizeLabel = new Label("Размер: не задан"); - fillMethodLabel = new Label("Способ заполнения: " + getFillMethodName(selectedFillMethod)); - panel.addComponent(sizeLabel); panel.addComponent(fillMethodLabel); - panel.addComponent(new Button("Задать размер", this::showCollectionSizeInputValueWindow)); - - panel.addComponent(new Button("Выбрать способ заполнения", this::selectFillMethod)); + panel.addComponent(new Button("Выбрать способ заполнения...", this::selectFillMethod)); panel.addComponent( - new Button("Настроить текущий способ заполнения", this::setupCurrentFillMethod)); + new Button("Настроить текущий способ заполнения...", this::setupCurrentFillMethod)); - panel.addComponent(new Button("Создать новую коллекцию", () -> updateMainCollection(state))); + panel.addComponent(new Button("Создать новую коллекцию", this::updateMainCollection)); panel.addComponent(new Button("Выйти", this::close)); setComponent(panel); } - private void showCollectionSizeInputValueWindow() { - InputValueWindow window = - new InputValueWindow( - gui, - "Размер коллекции", - "Введите размер коллекции:", - collectionSize > 0 ? String.valueOf(collectionSize) : null, - this::handleSizeEntered); - - window.showModal(); + private void setManualFillStrategy(ManualFillSettings settings) { + fillStrategy = new ManualFillStrategy(settings); } - private boolean handleSizeEntered(String value) { - try { - int enteredSize = Integer.parseInt(value); - if (enteredSize <= 0) { - MessageWindow.showModal(gui, "Размер должен быть больше 0."); - return false; - } - collectionSize = enteredSize; - sizeLabel.setText("Размер: " + collectionSize); - return true; - } catch (NumberFormatException e) { - MessageWindow.showModal(gui, "Введите целое число."); - return false; - } + private void setFileFillStrategy(FileFillSettings settings) { + fillStrategy = new FileFillStrategy(settings); + } + + private void setRandomFillStrategy(RandomFillSettings settings) { + fillStrategy = new RandomFillStrategy(settings); } private void setupCurrentFillMethod() { switch (selectedFillMethod) { - case CUSTOM -> { - customDataSettingsWindow.showModal(); - } + case CUSTOM -> manualDataSettingsWindow.showModal(); - case FILE -> - // TODO - MessageWindow.showModal(gui, "Не реализовано."); + case FILE -> fileDataSettingsWindow.showModal(); - case RANDOM -> - MessageWindow.showModal( - gui, "Для случайных данных дополнительные настройки не требуются."); + case RANDOM -> randomDataSettingsWindow.showModal(); } } @@ -105,6 +90,7 @@ public class CreateCollectionMenu extends BaseModalWindow { gui, fillMethod -> { selectedFillMethod = fillMethod; + fillMethodLabel.setText( "Способ заполнения: " + getFillMethodName(selectedFillMethod)); }); @@ -112,57 +98,22 @@ public class CreateCollectionMenu extends BaseModalWindow { menu.showModal(); } - private void updateMainCollection(AppState state) { - if (collectionSize <= 0) { - MessageWindow.showModal(gui, "Сначала задайте размер коллекции."); + private void updateMainCollection() { + if (fillStrategy == null) { + MessageWindow.showModal(gui, "Сначала настройте способ заполнения."); + return; } - switch (selectedFillMethod) { - case CUSTOM: - createCollectionFromCustomData(state); - break; - - case RANDOM: - createCollectionFromRandomData(state); - break; - - case FILE: - createCollectionFromFileData(state); - break; - } - } - - private void createCollectionFromCustomData(AppState state) { - MyList customDataCollection = state.getCustomDataCollection(); - - MyList mainCollection = new MyLinkedList<>(); - - for (int i = 0; i < customDataCollection.size(); i++) { - mainCollection.add(customDataCollection.get(i)); - } - - state.setMainCollection(mainCollection); + state.setMainCollection(fillStrategy.create()); MessageWindow.showModal(gui, "Коллекция успешно создана."); } - private void createCollectionFromRandomData(AppState state) { - // TODO - MessageWindow.showModal(gui, "Не реализовано."); - } - - private void createCollectionFromFileData(AppState state) { - // TODO - MessageWindow.showModal(gui, "Не реализовано."); - } - private String getFillMethodName(FillMethod fillMethod) { return switch (fillMethod) { case CUSTOM -> "Пользовательские данные"; - case RANDOM -> "Случайные данные"; - case FILE -> "Данные из файла"; }; } diff --git a/app/src/main/java/ru/project/ui/creation/FillStrategy.java b/app/src/main/java/ru/project/ui/creation/FillStrategy.java new file mode 100644 index 0000000..f4931d8 --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/FillStrategy.java @@ -0,0 +1,9 @@ +package ru.project.ui.creation; + +import ru.project.collection.MyList; +import ru.project.student.Student; + +public interface FillStrategy { + + MyList create(); +} diff --git a/app/src/main/java/ru/project/ui/creation/file/FileDataSettingsWindow.java b/app/src/main/java/ru/project/ui/creation/file/FileDataSettingsWindow.java new file mode 100644 index 0000000..bf1b7d4 --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/file/FileDataSettingsWindow.java @@ -0,0 +1,78 @@ +package ru.project.ui.creation.file; + +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import com.googlecode.lanterna.gui2.dialogs.FileDialog; +import com.googlecode.lanterna.gui2.dialogs.FileDialogBuilder; +import java.io.File; +import java.util.function.Consumer; +import ru.project.ui.base.BaseModalWindow; + +public class FileDataSettingsWindow extends BaseModalWindow { + + // private final StudentBuilder studentBuilder; + + private final FileFillSettings settings; + private final Consumer onApply; + + private final Label filePathLabel; + + public FileDataSettingsWindow(WindowBasedTextGUI gui, Consumer onApply) { + + super("Данные из файла", gui); + + this.onApply = onApply; + + this.settings = new FileFillSettings(); + // this.studentBuilder = new StudentBuilder(); + + Panel panel = new Panel(); + panel.setLayoutManager(new LinearLayout()); + + filePathLabel = new Label("Абсолютный путь: " + getFilePathLabel()); + + panel.addComponent(filePathLabel); + + panel.addComponent(new Button("Выбрать файл", this::selectFile)); + + panel.addComponent(new Button("Применить", this::apply)); + + panel.addComponent(new Button("Выйти", this::close)); + + setComponent(panel); + } + + private void selectFile() { + FileDialog fileDialog = + new FileDialogBuilder() + .setTitle("Выбор файла") + .setDescription("Выберите файл:") + .setActionLabel("Открыть") + .build(); + + File selectedFile = fileDialog.showDialog(gui); + + if (selectedFile == null) { + return; + } + + settings.setAbsoluteFilePath(selectedFile.getAbsolutePath()); + + filePathLabel.setText("Абсолютный путь: " + settings.getAbsoluteFilePath()); + } + + private void apply() { + onApply.accept(settings); + + close(); + } + + private String getFilePathLabel() { + String filePath = settings.getAbsoluteFilePath(); + + return filePath == null || filePath.isBlank() ? "не задан" : filePath; + } +} diff --git a/app/src/main/java/ru/project/ui/creation/file/FileFillSettings.java b/app/src/main/java/ru/project/ui/creation/file/FileFillSettings.java new file mode 100644 index 0000000..db7e9cc --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/file/FileFillSettings.java @@ -0,0 +1,18 @@ +package ru.project.ui.creation.file; + +public class FileFillSettings { + + private String absoluteFilePath; + + public FileFillSettings() { + this.absoluteFilePath = null; + } + + public String getAbsoluteFilePath() { + return absoluteFilePath; + } + + public void setAbsoluteFilePath(String absoluteFilePath) { + this.absoluteFilePath = absoluteFilePath; + } +} diff --git a/app/src/main/java/ru/project/ui/creation/file/FileFillStrategy.java b/app/src/main/java/ru/project/ui/creation/file/FileFillStrategy.java new file mode 100644 index 0000000..85cca03 --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/file/FileFillStrategy.java @@ -0,0 +1,37 @@ +package ru.project.ui.creation.file; + +import java.io.IOException; +import java.nio.file.Path; +import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.io.StudentCsvReader; +import ru.project.student.Student; +import ru.project.ui.creation.FillStrategy; + +public class FileFillStrategy implements FillStrategy { + + private final FileFillSettings settings; + + public FileFillStrategy(FileFillSettings settings) { + this.settings = settings; + } + + @Override + public MyList create() { + MyList collection = new MyLinkedList<>(); + + Path path = Path.of(settings.getAbsoluteFilePath()); + + try (StudentCsvReader reader = new StudentCsvReader(path)) { + + while (reader.hasNext()) { + collection.add(reader.next()); + } + + } catch (IOException e) { + throw new RuntimeException("Не удалось прочитать коллекцию из файла.", e); + } + + return collection; + } +} diff --git a/app/src/main/java/ru/project/ui/creation/CustomDataSettingsWindow.java b/app/src/main/java/ru/project/ui/creation/manual/ManualDataSettingsWindow.java similarity index 64% rename from app/src/main/java/ru/project/ui/creation/CustomDataSettingsWindow.java rename to app/src/main/java/ru/project/ui/creation/manual/ManualDataSettingsWindow.java index 5f0aa10..dd02eeb 100644 --- a/app/src/main/java/ru/project/ui/creation/CustomDataSettingsWindow.java +++ b/app/src/main/java/ru/project/ui/creation/manual/ManualDataSettingsWindow.java @@ -1,32 +1,32 @@ -package ru.project.ui.creation; +package ru.project.ui.creation.manual; import com.googlecode.lanterna.gui2.Button; import com.googlecode.lanterna.gui2.Label; import com.googlecode.lanterna.gui2.LinearLayout; import com.googlecode.lanterna.gui2.Panel; import com.googlecode.lanterna.gui2.WindowBasedTextGUI; -import java.util.function.Supplier; +import java.util.function.Consumer; import ru.project.collection.MyLinkedList; import ru.project.collection.MyList; -import ru.project.model.AppState; import ru.project.student.Student; import ru.project.student.StudentBuilder; import ru.project.ui.base.BaseModalWindow; import ru.project.ui.common.CollectionDisplayWindow; +import ru.project.ui.common.CollectionSizeInputWindow; import ru.project.ui.common.InputValueWindow; import ru.project.ui.common.MessageWindow; -public class CustomDataSettingsWindow extends BaseModalWindow { +public class ManualDataSettingsWindow extends BaseModalWindow { - private final AppState state; - private final Supplier requestedCollectionSize; private final StudentBuilder studentBuilder; - private String groupNumber; - private Double averageGrade; - private Integer recordBookNumber; - private Integer selectedObjectIndex; + private Integer lastRequestedCollectionSize; + private String lastRequestedGroupNumber; + private Double lastRequestedAverageGrade; + private Integer lastRequestedRecordBookNumber; + private Integer lastRequestedSelectedObjectIndex; + private final Label sizeLabel; private final Label groupNumberLabel; private final Label averageGradeLabel; private final Label recordBookNumberLabel; @@ -34,19 +34,23 @@ public class CustomDataSettingsWindow extends BaseModalWindow { private MyList preparedCustomDataCollection; - public CustomDataSettingsWindow( - WindowBasedTextGUI gui, AppState state, Supplier collectionSize) { + private final ManualFillSettings settings; + private final Consumer onApply; + + public ManualDataSettingsWindow(WindowBasedTextGUI gui, Consumer onApply) { super("Настройка пользовательских данных", gui); - this.state = state; - this.requestedCollectionSize = collectionSize; + this.onApply = onApply; + this.settings = new ManualFillSettings(); this.studentBuilder = new StudentBuilder(); Panel panel = new Panel(); panel.setLayoutManager(new LinearLayout()); + sizeLabel = new Label("Размер: не задан"); + groupNumberLabel = new Label("Номер группы: не задан"); averageGradeLabel = new Label("Средний балл: не задан"); @@ -55,11 +59,14 @@ public class CustomDataSettingsWindow extends BaseModalWindow { indexLabel = new Label("Индекс объекта: не задан"); + panel.addComponent(sizeLabel); panel.addComponent(groupNumberLabel); panel.addComponent(averageGradeLabel); panel.addComponent(recordBookNumberLabel); panel.addComponent(indexLabel); + panel.addComponent(new Button("Задать размер", this::showCollectionSizeInputWindow)); + panel.addComponent(new Button("Задать номер группы", this::setGroupNumber)); panel.addComponent(new Button("Задать средний балл", this::setAverageGrade)); @@ -79,25 +86,65 @@ public class CustomDataSettingsWindow extends BaseModalWindow { setComponent(panel); } + private void showCollectionSizeInputWindow() { + CollectionSizeInputWindow window = + new CollectionSizeInputWindow( + gui, preparedCustomDataCollection.size(), this::handleCollectionSizeEntered); + + window.showModal(); + } + + private void handleCollectionSizeEntered(int size) { + MyList resizedCollection = new MyLinkedList<>(); + + int currentCollectionSize = preparedCustomDataCollection.size(); + + int elementsToCopy = Math.min(currentCollectionSize, size); + + for (int i = 0; i < elementsToCopy; i++) { + resizedCollection.add(preparedCustomDataCollection.get(i)); + } + + for (int i = elementsToCopy; i < size; i++) { + resizedCollection.add(null); + } + + preparedCustomDataCollection = resizedCollection; + lastRequestedCollectionSize = size; + + if (lastRequestedSelectedObjectIndex != null + && lastRequestedSelectedObjectIndex >= lastRequestedCollectionSize) { + lastRequestedSelectedObjectIndex = null; + indexLabel.setText("Индекс объекта: не задан"); + } + + sizeLabel.setText("Размер: " + lastRequestedCollectionSize); + } + @Override public void showModal() { prepareCustomDataCollection(); + super.showModal(); } private void prepareCustomDataCollection() { + MyList currentCustomDataCollection = settings.getCollection(); + preparedCustomDataCollection = new MyLinkedList<>(); - int targetCollectionSize = requestedCollectionSize.get(); - int currentCustomDataCollectionSize = state.getCustomDataCollection().size(); + int currentCollectionSize = currentCustomDataCollection.size(); - int elementsToCopy = Math.min(currentCustomDataCollectionSize, targetCollectionSize); + int targetSize = + lastRequestedCollectionSize == null ? currentCollectionSize : lastRequestedCollectionSize; + + int elementsToCopy = Math.min(currentCollectionSize, targetSize); for (int i = 0; i < elementsToCopy; i++) { - preparedCustomDataCollection.add(state.getCustomDataCollection().get(i)); + preparedCustomDataCollection.add(currentCustomDataCollection.get(i)); } - for (int i = elementsToCopy; i < targetCollectionSize; i++) { + for (int i = elementsToCopy; i < targetSize; i++) { preparedCustomDataCollection.add(null); } } @@ -108,17 +155,17 @@ public class CustomDataSettingsWindow extends BaseModalWindow { gui, "Номер группы", "Введите номер группы (например, A12):", - groupNumber, + lastRequestedGroupNumber, value -> { if (!value.matches(Student.getGroupNumberPattern())) { MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12."); return false; } - groupNumber = value; + lastRequestedGroupNumber = value; studentBuilder.setGroupNumber(value); - groupNumberLabel.setText("Номер группы: " + groupNumber); + groupNumberLabel.setText("Номер группы: " + lastRequestedGroupNumber); return true; }); @@ -132,7 +179,7 @@ public class CustomDataSettingsWindow extends BaseModalWindow { gui, "Средний балл", "Введите средний балл от 0.0 до 5.0:", - averageGrade == null ? null : String.valueOf(averageGrade), + lastRequestedAverageGrade == null ? null : String.valueOf(lastRequestedAverageGrade), value -> { try { double parsedValue = Double.parseDouble(value); @@ -145,11 +192,11 @@ public class CustomDataSettingsWindow extends BaseModalWindow { return false; } - averageGrade = parsedValue; + lastRequestedAverageGrade = parsedValue; studentBuilder.setAverageGrade(parsedValue); - averageGradeLabel.setText("Средний балл: " + averageGrade); + averageGradeLabel.setText("Средний балл: " + lastRequestedAverageGrade); return true; @@ -169,7 +216,9 @@ public class CustomDataSettingsWindow extends BaseModalWindow { gui, "Номер зачётной книжки", "Введите номер зачётной книжки:", - recordBookNumber == null ? null : String.valueOf(recordBookNumber), + lastRequestedRecordBookNumber == null + ? null + : String.valueOf(lastRequestedRecordBookNumber), value -> { try { int parsedValue = Integer.parseInt(value); @@ -181,11 +230,12 @@ public class CustomDataSettingsWindow extends BaseModalWindow { return false; } - recordBookNumber = parsedValue; + lastRequestedRecordBookNumber = parsedValue; studentBuilder.setRecordBookNumber(parsedValue); - recordBookNumberLabel.setText("Номер зачётной книжки: " + recordBookNumber); + recordBookNumberLabel.setText( + "Номер зачётной книжки: " + lastRequestedRecordBookNumber); return true; @@ -205,34 +255,31 @@ public class CustomDataSettingsWindow extends BaseModalWindow { gui, "Индекс объекта", "Введите индекс объекта:", - selectedObjectIndex == null ? null : String.valueOf(selectedObjectIndex), + lastRequestedSelectedObjectIndex == null + ? null + : String.valueOf(lastRequestedSelectedObjectIndex), value -> { try { int index = Integer.parseInt(value); if (index < 0) { MessageWindow.showModal(gui, "Индекс объекта не может быть отрицательным."); - return false; } - int size = requestedCollectionSize.get(); - - if (index >= size) { + if (index >= preparedCustomDataCollection.size()) { MessageWindow.showModal(gui, "Индекс должен быть меньше размера коллекции."); - return false; } - selectedObjectIndex = index; + lastRequestedSelectedObjectIndex = index; - indexLabel.setText("Индекс объекта: " + selectedObjectIndex); + indexLabel.setText("Индекс объекта: " + lastRequestedSelectedObjectIndex); return true; } catch (NumberFormatException e) { MessageWindow.showModal(gui, "Введите целое число."); - return false; } }); @@ -246,28 +293,28 @@ public class CustomDataSettingsWindow extends BaseModalWindow { return; } - if (selectedObjectIndex == null) { + if (lastRequestedSelectedObjectIndex == null) { MessageWindow.showModal(gui, "Сначала задайте индекс объекта."); return; } - if (selectedObjectIndex >= preparedCustomDataCollection.size()) { + if (lastRequestedSelectedObjectIndex >= preparedCustomDataCollection.size()) { MessageWindow.showModal(gui, "Индекс объекта выходит за границы подготовленной коллекции."); return; } Student student = studentBuilder.build(); - preparedCustomDataCollection.set(selectedObjectIndex, student); + preparedCustomDataCollection.set(lastRequestedSelectedObjectIndex, student); MessageWindow.showModal(gui, "Данные объекта успешно заданы."); } private void apply() { - state.setCustomDataCollection(preparedCustomDataCollection); - prepareCustomDataCollection(); + settings.setCollection(preparedCustomDataCollection); + onApply.accept(settings); - MessageWindow.showModal(gui, "Пользовательские данные успешно применены."); + MessageWindow.showModal(gui, "Настройки создания коллекции вручную успешно применены."); } private void preview() { @@ -284,6 +331,8 @@ public class CustomDataSettingsWindow extends BaseModalWindow { } private boolean hasStudentData() { - return groupNumber != null && averageGrade != null && recordBookNumber != null; + return lastRequestedGroupNumber != null + && lastRequestedAverageGrade != null + && lastRequestedRecordBookNumber != null; } } diff --git a/app/src/main/java/ru/project/ui/creation/manual/ManualFillSettings.java b/app/src/main/java/ru/project/ui/creation/manual/ManualFillSettings.java new file mode 100644 index 0000000..ff82f46 --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/manual/ManualFillSettings.java @@ -0,0 +1,22 @@ +package ru.project.ui.creation.manual; + +import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.student.Student; + +public class ManualFillSettings { + + private MyList manualDataCollection; + + public ManualFillSettings() { + this.manualDataCollection = new MyLinkedList<>(); + } + + public MyList getCollection() { + return manualDataCollection; + } + + public void setCollection(MyList collection) { + this.manualDataCollection = collection; + } +} diff --git a/app/src/main/java/ru/project/ui/creation/manual/ManualFillStrategy.java b/app/src/main/java/ru/project/ui/creation/manual/ManualFillStrategy.java new file mode 100644 index 0000000..1a5c54e --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/manual/ManualFillStrategy.java @@ -0,0 +1,28 @@ +package ru.project.ui.creation.manual; + +import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; +import ru.project.student.Student; +import ru.project.ui.creation.FillStrategy; + +public class ManualFillStrategy implements FillStrategy { + + private final ManualFillSettings settings; + + public ManualFillStrategy(ManualFillSettings settings) { + this.settings = settings; + } + + @Override + public MyList create() { + MyList mainCollection = new MyLinkedList<>(); + + MyList manualDataCollection = settings.getCollection(); + + for (int i = 0; i < manualDataCollection.size(); i++) { + mainCollection.add(manualDataCollection.get(i)); + } + + return mainCollection; + } +} diff --git a/app/src/main/java/ru/project/ui/creation/random/RandomDataSettingsWindow.java b/app/src/main/java/ru/project/ui/creation/random/RandomDataSettingsWindow.java new file mode 100644 index 0000000..5cc80c7 --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/random/RandomDataSettingsWindow.java @@ -0,0 +1,361 @@ +package ru.project.ui.creation.random; + +import com.googlecode.lanterna.gui2.Button; +import com.googlecode.lanterna.gui2.Label; +import com.googlecode.lanterna.gui2.LinearLayout; +import com.googlecode.lanterna.gui2.Panel; +import com.googlecode.lanterna.gui2.WindowBasedTextGUI; +import java.util.function.Consumer; +import ru.project.ui.base.BaseModalWindow; +import ru.project.ui.common.InputValueWindow; +import ru.project.ui.common.MessageWindow; + +public class RandomDataSettingsWindow extends BaseModalWindow { + + private final RandomFillSettings settings; + private final Consumer onApply; + + private final Label collectionSizeLabel; + private final Label minGroupNumberLabel; + private final Label maxGroupNumberLabel; + private final Label minAverageGradeLabel; + private final Label maxAverageGradeLabel; + private final Label minRecordBookNumberLabel; + private final Label maxRecordBookNumberLabel; + + public RandomDataSettingsWindow(WindowBasedTextGUI gui, Consumer onApply) { + + super("Настройка случайного заполнения", gui); + + this.settings = new RandomFillSettings(); + this.onApply = onApply; + + Panel panel = new Panel(); + panel.setLayoutManager(new LinearLayout()); + + collectionSizeLabel = + new Label("Размер коллекции: " + getValueOrDefault(settings.getCollectionSize())); + + panel.addComponent(collectionSizeLabel); + minGroupNumberLabel = + new Label("Минимальный номер группы: " + getValueOrDefault(settings.getMinGroupNumber())); + + maxGroupNumberLabel = + new Label("Максимальный номер группы: " + getValueOrDefault(settings.getMaxGroupNumber())); + + minAverageGradeLabel = + new Label("Минимальный средний балл: " + getValueOrDefault(settings.getMinAverageGrade())); + + maxAverageGradeLabel = + new Label("Максимальный средний балл: " + getValueOrDefault(settings.getMaxAverageGrade())); + + minRecordBookNumberLabel = + new Label( + "Минимальный номер зачётной книжки: " + + getValueOrDefault(settings.getMinRecordBookNumber())); + + maxRecordBookNumberLabel = + new Label( + "Максимальный номер зачётной книжки: " + + getValueOrDefault(settings.getMaxRecordBookNumber())); + + panel.addComponent(minGroupNumberLabel); + panel.addComponent(maxGroupNumberLabel); + panel.addComponent(minAverageGradeLabel); + panel.addComponent(maxAverageGradeLabel); + panel.addComponent(minRecordBookNumberLabel); + panel.addComponent(maxRecordBookNumberLabel); + + panel.addComponent(new Button("Задать размер коллекции...", this::setCollectionSize)); + + panel.addComponent(new Button("Задать минимальный номер группы", this::setMinGroupNumber)); + + panel.addComponent(new Button("Задать максимальный номер группы", this::setMaxGroupNumber)); + + panel.addComponent(new Button("Задать минимальный средний балл", this::setMinAverageGrade)); + + panel.addComponent(new Button("Задать максимальный средний балл", this::setMaxAverageGrade)); + + panel.addComponent( + new Button("Задать минимальный номер зачётной книжки", this::setMinRecordBookNumber)); + + panel.addComponent( + new Button("Задать максимальный номер зачётной книжки", this::setMaxRecordBookNumber)); + + panel.addComponent(new Button("Применить", this::apply)); + + panel.addComponent(new Button("Назад", this::close)); + + setComponent(panel); + } + + private void setCollectionSize() { + InputValueWindow window = + new InputValueWindow( + gui, + "Размер коллекции", + "Введите размер коллекции:", + settings.getCollectionSize() == null + ? null + : String.valueOf(settings.getCollectionSize()), + value -> { + try { + int collectionSize = Integer.parseInt(value); + + if (collectionSize <= 0) { + MessageWindow.showModal(gui, "Размер коллекции должен быть больше 0."); + + return false; + } + + settings.setCollectionSize(collectionSize); + + collectionSizeLabel.setText("Размер коллекции: " + collectionSize); + + return true; + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите целое число."); + + return false; + } + }); + + window.showModal(); + } + + private void setMinGroupNumber() { + showGroupNumberInput( + "Минимальный номер группы", + settings.getMinGroupNumber(), + value -> { + if (!value.matches("[A-Z]\\d{2}")) { + + MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12."); + + return false; + } + + if (settings.getMaxGroupNumber() != null + && value.compareTo(settings.getMaxGroupNumber()) > 0) { + + MessageWindow.showModal( + gui, "Минимальный номер группы не может быть больше максимального."); + + return false; + } + + settings.setMinGroupNumber(value); + + minGroupNumberLabel.setText("Минимальный номер группы: " + value); + + return true; + }); + } + + private void setMaxGroupNumber() { + showGroupNumberInput( + "Максимальный номер группы", + settings.getMaxGroupNumber(), + value -> { + if (!value.matches("[A-Z]\\d{2}")) { + + MessageWindow.showModal(gui, "Номер группы должен соответствовать формату A12."); + + return false; + } + + if (settings.getMinGroupNumber() != null + && value.compareTo(settings.getMinGroupNumber()) < 0) { + + MessageWindow.showModal( + gui, "Максимальный номер группы не может быть меньше минимального."); + + return false; + } + + settings.setMaxGroupNumber(value); + + maxGroupNumberLabel.setText("Максимальный номер группы: " + value); + + return true; + }); + } + + private void showGroupNumberInput( + String title, String initialValue, java.util.function.Predicate onSave) { + + InputValueWindow window = + new InputValueWindow( + gui, title, "Введите номер группы (например, A12):", initialValue, onSave); + + window.showModal(); + } + + private void setMinAverageGrade() { + showAverageGradeInput( + "Минимальный средний балл", + settings.getMinAverageGrade(), + value -> { + if (settings.getMaxAverageGrade() != null && value > settings.getMaxAverageGrade()) { + + MessageWindow.showModal( + gui, "Минимальный средний балл не может быть больше максимального."); + + return false; + } + + settings.setMinAverageGrade(value); + + minAverageGradeLabel.setText("Минимальный средний балл: " + value); + + return true; + }); + } + + private void setMaxAverageGrade() { + showAverageGradeInput( + "Максимальный средний балл", + settings.getMaxAverageGrade(), + value -> { + if (settings.getMinAverageGrade() != null && value < settings.getMinAverageGrade()) { + + MessageWindow.showModal( + gui, "Максимальный средний балл не может быть меньше минимального."); + + return false; + } + + settings.setMaxAverageGrade(value); + + maxAverageGradeLabel.setText("Максимальный средний балл: " + value); + + return true; + }); + } + + private void showAverageGradeInput( + String title, Double initialValue, java.util.function.Predicate onSave) { + + InputValueWindow window = + new InputValueWindow( + gui, + title, + "Введите значение от " + "0.0 до 5.0:", + initialValue == null ? null : String.valueOf(initialValue), + value -> { + try { + double parsedValue = Double.parseDouble(value); + + if (parsedValue < ru.project.student.Student.getMinAverageGrade() + || parsedValue > ru.project.student.Student.getMaxAverageGrade()) { + + MessageWindow.showModal(gui, "Средний балл должен быть от 0.0 до 5.0."); + + return false; + } + + return onSave.test(parsedValue); + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите корректное число."); + + return false; + } + }); + + window.showModal(); + } + + private void setMinRecordBookNumber() { + showRecordBookNumberInput( + "Минимальный номер зачётной книжки", + settings.getMinRecordBookNumber(), + value -> { + if (settings.getMaxRecordBookNumber() != null + && value > settings.getMaxRecordBookNumber()) { + + MessageWindow.showModal( + gui, "Минимальный номер зачётной книжки не может быть больше максимального."); + + return false; + } + + settings.setMinRecordBookNumber(value); + + minRecordBookNumberLabel.setText("Минимальный номер зачётной книжки: " + value); + + return true; + }); + } + + private void setMaxRecordBookNumber() { + showRecordBookNumberInput( + "Максимальный номер зачётной книжки", + settings.getMaxRecordBookNumber(), + value -> { + if (settings.getMinRecordBookNumber() != null + && value < settings.getMinRecordBookNumber()) { + + MessageWindow.showModal( + gui, "Максимальный номер зачётной книжки не может быть меньше минимального."); + + return false; + } + + settings.setMaxRecordBookNumber(value); + + maxRecordBookNumberLabel.setText("Максимальный номер зачётной книжки: " + value); + + return true; + }); + } + + private void showRecordBookNumberInput( + String title, Integer initialValue, java.util.function.Predicate onSave) { + + InputValueWindow window = + new InputValueWindow( + gui, + title, + "Введите номер зачётной книжки:", + initialValue == null ? null : String.valueOf(initialValue), + value -> { + try { + int parsedValue = Integer.parseInt(value); + + if (parsedValue < ru.project.student.Student.getMinRecordBookNumber()) { + + MessageWindow.showModal(gui, "Номер зачётной книжки должен быть положительным."); + + return false; + } + + return onSave.test(parsedValue); + + } catch (NumberFormatException e) { + MessageWindow.showModal(gui, "Введите целое число."); + + return false; + } + }); + + window.showModal(); + } + + private void apply() { + if (settings.getCollectionSize() == null) { + MessageWindow.showModal(gui, "Сначала задайте размер коллекции."); + + return; + } + + onApply.accept(settings); + + MessageWindow.showModal(gui, "Настройки создания коллекции случайно успешно применены."); + } + + private String getValueOrDefault(Object value) { + return value == null ? "не задан" : String.valueOf(value); + } +} diff --git a/app/src/main/java/ru/project/ui/creation/random/RandomFillSettings.java b/app/src/main/java/ru/project/ui/creation/random/RandomFillSettings.java new file mode 100644 index 0000000..22befaf --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/random/RandomFillSettings.java @@ -0,0 +1,84 @@ +package ru.project.ui.creation.random; + +public class RandomFillSettings { + + private Integer collectionSize; + + private String minGroupNumber; + private String maxGroupNumber; + + private Double minAverageGrade; + private Double maxAverageGrade; + + private Integer minRecordBookNumber; + private Integer maxRecordBookNumber; + + public RandomFillSettings() { + this.collectionSize = null; + + this.minGroupNumber = null; + this.maxGroupNumber = null; + + this.minAverageGrade = null; + this.maxAverageGrade = null; + + this.minRecordBookNumber = null; + this.maxRecordBookNumber = null; + } + + public Integer getCollectionSize() { + return collectionSize; + } + + public void setCollectionSize(Integer collectionSize) { + this.collectionSize = collectionSize; + } + + public String getMinGroupNumber() { + return minGroupNumber; + } + + public void setMinGroupNumber(String minGroupNumber) { + this.minGroupNumber = minGroupNumber; + } + + public String getMaxGroupNumber() { + return maxGroupNumber; + } + + public void setMaxGroupNumber(String maxGroupNumber) { + this.maxGroupNumber = maxGroupNumber; + } + + public Double getMinAverageGrade() { + return minAverageGrade; + } + + public void setMinAverageGrade(Double minAverageGrade) { + this.minAverageGrade = minAverageGrade; + } + + public Double getMaxAverageGrade() { + return maxAverageGrade; + } + + public void setMaxAverageGrade(Double maxAverageGrade) { + this.maxAverageGrade = maxAverageGrade; + } + + public Integer getMinRecordBookNumber() { + return minRecordBookNumber; + } + + public void setMinRecordBookNumber(Integer minRecordBookNumber) { + this.minRecordBookNumber = minRecordBookNumber; + } + + public Integer getMaxRecordBookNumber() { + return maxRecordBookNumber; + } + + public void setMaxRecordBookNumber(Integer maxRecordBookNumber) { + this.maxRecordBookNumber = maxRecordBookNumber; + } +} diff --git a/app/src/main/java/ru/project/ui/creation/random/RandomFillStrategy.java b/app/src/main/java/ru/project/ui/creation/random/RandomFillStrategy.java new file mode 100644 index 0000000..77dd072 --- /dev/null +++ b/app/src/main/java/ru/project/ui/creation/random/RandomFillStrategy.java @@ -0,0 +1,46 @@ +package ru.project.ui.creation.random; + +import ru.project.collection.MyList; +import ru.project.random.RandomStudentGenerator; +import ru.project.student.Student; +import ru.project.ui.creation.FillStrategy; + +public class RandomFillStrategy implements FillStrategy { + + private final RandomFillSettings settings; + + public RandomFillStrategy(RandomFillSettings settings) { + this.settings = settings; + } + + @Override + public MyList create() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + + if (settings.getMinGroupNumber() != null) { + generator.setMinGroupNumber(settings.getMinGroupNumber()); + } + + if (settings.getMaxGroupNumber() != null) { + generator.setMaxGroupNumber(settings.getMaxGroupNumber()); + } + + if (settings.getMinAverageGrade() != null) { + generator.setMinAverageGrade(settings.getMinAverageGrade()); + } + + if (settings.getMaxAverageGrade() != null) { + generator.setMaxAverageGrade(settings.getMaxAverageGrade()); + } + + if (settings.getMinRecordBookNumber() != null) { + generator.setMinRecordBookNumber(settings.getMinRecordBookNumber()); + } + + if (settings.getMaxRecordBookNumber() != null) { + generator.setMaxRecordBookNumber(settings.getMaxRecordBookNumber()); + } + + return generator.generate(settings.getCollectionSize()); + } +} diff --git a/app/src/main/java/ru/project/ui/main/MainMenu.java b/app/src/main/java/ru/project/ui/main/MainMenu.java deleted file mode 100644 index ff4899e..0000000 --- a/app/src/main/java/ru/project/ui/main/MainMenu.java +++ /dev/null @@ -1,80 +0,0 @@ -package ru.project.ui.main; - -import com.googlecode.lanterna.gui2.ActionListBox; -import com.googlecode.lanterna.gui2.WindowBasedTextGUI; -import ru.project.collection.MyList; -import ru.project.model.AppState; -import ru.project.student.Student; -import ru.project.ui.base.BaseModalWindow; -import ru.project.ui.common.CollectionDisplayWindow; -import ru.project.ui.common.MessageWindow; -import ru.project.ui.creation.CreateCollectionMenu; -import ru.project.ui.sorting.SortCollectionMenu; - -public class MainMenu extends BaseModalWindow { - - private final ActionListBox menu; - - private final BaseModalWindow createCollectionMenu; - private final BaseModalWindow sortCollectionMenu; - - public MainMenu(WindowBasedTextGUI gui, AppState state) { - super("Главное меню", gui); - - this.createCollectionMenu = new CreateCollectionMenu(gui, state); - this.sortCollectionMenu = new SortCollectionMenu(gui, state); - - menu = new ActionListBox(); - - menu.addItem("Показать текущую коллекцию", () -> showMainCollection(state)); - - menu.addItem("Меню создания новой коллекции", this::showCreateCollectionMenu); - - menu.addItem("Меню сохранения текущей коллекции в файл", this::saveCollection); - - menu.addItem("Меню сортировки текущей коллекции", this::sortCollection); - - menu.addItem("Меню поиска элемента в текущей коллекции", this::searchCollection); - - menu.addItem("Меню подсчёта количества вхождений элемента", this::countOccurrences); - - menu.addItem("Завершить выполнение программы", this::exit); - - setComponent(menu); - } - - private void showMainCollection(AppState state) { - MyList mainCollection = state.getMainCollection(); - - BaseModalWindow window = new CollectionDisplayWindow(gui, mainCollection); - - window.showModal(); - } - - private void showCreateCollectionMenu() { - createCollectionMenu.showModal(); - } - - private void saveCollection() { - // TODO - MessageWindow.showModal(gui, "Не реализовано."); - } - - private void sortCollection() { - sortCollectionMenu.showModal(); - } - - private void searchCollection() { - // TODO - MessageWindow.showModal(gui, "Не реализовано."); - } - - private void countOccurrences() { - // TODO - MessageWindow.showModal(gui, "Не реализовано."); - } - - private void exit() { - close(); - } -} diff --git a/app/src/test/java/ru/project/random/RandomStudentGeneratorTest.java b/app/src/test/java/ru/project/random/RandomStudentGeneratorTest.java index 2b5a4be..b91471a 100644 --- a/app/src/test/java/ru/project/random/RandomStudentGeneratorTest.java +++ b/app/src/test/java/ru/project/random/RandomStudentGeneratorTest.java @@ -1,157 +1,163 @@ package ru.project.random; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.regex.Pattern; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import ru.project.collection.MyLinkedList; import ru.project.collection.MyList; import ru.project.student.Student; -import java.util.regex.Pattern; -import static org.junit.jupiter.api.Assertions.*; -public class RandomStudentGeneratorTest -{ - private static final Pattern GROUP_PATTERN = Pattern.compile("^[A-Z][0-9]{2}$"); - private static final double MIN_GRADE = 0.0; - private static final double MAX_GRADE = 5.0; - @Test - void shouldTestgenerate_withoutConstraints_returnsCorrectSizeAndValidStudents() { - int size = 100; - RandomStudentGenerator generator = new RandomStudentGenerator(); - MyList students = generator.generate(size); - assertEquals(size, students.size(), "Размер списка должен совпадать с запрошенным"); - for (int i = 0; i < students.size(); i++) { - Student s = students.get(i); - assertTrue(GROUP_PATTERN.matcher(s.getGroupNumber()).matches(), - "groupNumber должен соответствовать шаблону A00–Z99"); - assertTrue(s.getAverageGrade() >= MIN_GRADE && s.getAverageGrade() <= MAX_GRADE, - "averageGrade должен быть в диапазоне [0.0, 5.0]"); - assertTrue(s.getRecordBookNumber() >= 1, - "recordBookNumber должен быть >= 1"); - } - } - @Test - void shouldTestgenerate_withGroupRange_respectsLexicographicalBounds() { - RandomStudentGenerator generator = new RandomStudentGenerator() - .setMinGroupNumber("A00") - .setMaxGroupNumber("B99"); - MyList students = generator.generate(200); - assertEquals(200, students.size()); - for (int i = 0; i < students.size(); i++) { - Student s = students.get(i); - String g = s.getGroupNumber(); - assertTrue(g.compareTo("A00") >= 0, "Группа не должна быть меньше A00"); - assertTrue(g.compareTo("B99") <= 0, "Группа не должна быть больше B99"); - assertTrue(GROUP_PATTERN.matcher(g).matches(), "Группа должна соответствовать шаблону"); - } - } - @Test - void shouldTestgenerate_withGradeRange_respectsBounds() { - double minGrade = 3.0; - double maxGrade = 4.0; - RandomStudentGenerator generator = new RandomStudentGenerator() - .setMinAverageGrade(minGrade) - .setMaxAverageGrade(maxGrade); - MyList students = generator.generate(100); +public class RandomStudentGeneratorTest { + private static final Pattern GROUP_PATTERN = Pattern.compile("^[A-Z][0-9]{2}$"); + private static final double MIN_GRADE = 0.0; + private static final double MAX_GRADE = 5.0; - for (int i = 0; i < students.size(); i++) { - Student s = students.get(i); - double g = s.getAverageGrade(); - assertTrue(g >= minGrade && g <= maxGrade, - "averageGrade должен быть в заданном диапазоне"); - } + @Test + void shouldTestgenerate_withoutConstraints_returnsCorrectSizeAndValidStudents() { + int size = 100; + RandomStudentGenerator generator = new RandomStudentGenerator(); + MyList students = generator.generate(size); + assertEquals(size, students.size(), "Размер списка должен совпадать с запрошенным"); + for (int i = 0; i < students.size(); i++) { + Student s = students.get(i); + assertTrue( + GROUP_PATTERN.matcher(s.getGroupNumber()).matches(), + "groupNumber должен соответствовать шаблону A00–Z99"); + assertTrue( + s.getAverageGrade() >= MIN_GRADE && s.getAverageGrade() <= MAX_GRADE, + "averageGrade должен быть в диапазоне [0.0, 5.0]"); + assertTrue(s.getRecordBookNumber() >= 1, "recordBookNumber должен быть >= 1"); } - @Test - void shouldTestgenerate_withRecordBookRange_respectsBounds() { - int minRec = 10_000; - int maxRec = 20_000; + } - RandomStudentGenerator generator = new RandomStudentGenerator() - .setMinRecordBookNumber(minRec) - .setMaxRecordBookNumber(maxRec); - MyList students = generator.generate(100); + @Test + void shouldTestgenerate_withGroupRange_respectsLexicographicalBounds() { + RandomStudentGenerator generator = + new RandomStudentGenerator().setMinGroupNumber("A00").setMaxGroupNumber("B99"); + MyList students = generator.generate(200); + assertEquals(200, students.size()); + for (int i = 0; i < students.size(); i++) { + Student s = students.get(i); + String g = s.getGroupNumber(); + assertTrue(g.compareTo("A00") >= 0, "Группа не должна быть меньше A00"); + assertTrue(g.compareTo("B99") <= 0, "Группа не должна быть больше B99"); + assertTrue(GROUP_PATTERN.matcher(g).matches(), "Группа должна соответствовать шаблону"); + } + } - for (int i = 0; i < students.size(); i++) { - Student s = students.get(i); - int r = s.getRecordBookNumber(); - assertTrue(r >= minRec && r <= maxRec, - "recordBookNumber должен быть в заданном диапазоне"); - } - } + @Test + void shouldTestgenerate_withGradeRange_respectsBounds() { + double minGrade = 3.0; + double maxGrade = 4.0; - @Test - void shouldTestgenerate_negativeSize_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, () -> generator.generate(-1)); + RandomStudentGenerator generator = + new RandomStudentGenerator().setMinAverageGrade(minGrade).setMaxAverageGrade(maxGrade); + MyList students = generator.generate(100); + + for (int i = 0; i < students.size(); i++) { + Student s = students.get(i); + double g = s.getAverageGrade(); + assertTrue(g >= minGrade && g <= maxGrade, "averageGrade должен быть в заданном диапазоне"); } - @Test - void shouldTestsetMinGroupNumber_invalidPattern_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinGroupNumber("invalid")); + } + + @Test + void shouldTestgenerate_withRecordBookRange_respectsBounds() { + int minRec = 10_000; + int maxRec = 20_000; + + RandomStudentGenerator generator = + new RandomStudentGenerator().setMinRecordBookNumber(minRec).setMaxRecordBookNumber(maxRec); + MyList students = generator.generate(100); + + for (int i = 0; i < students.size(); i++) { + Student s = students.get(i); + int r = s.getRecordBookNumber(); + assertTrue(r >= minRec && r <= maxRec, "recordBookNumber должен быть в заданном диапазоне"); } - @Test - void shouldTestsetMaxGroupNumber_invalidPattern_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMaxGroupNumber("ZZ")); // не 2 цифры - } - @Test - void shouldTestsetMinMaxGroupNumber_minGreaterThanMax_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinGroupNumber("C00").setMaxGroupNumber("A99").generate(1)); - } - @Test - void shouldTestsetMinAverageGrade_outOfRange_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinAverageGrade(-0.1)); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinAverageGrade(5.1)); - } - @Test - void shouldTestsetMaxAverageGrade_outOfRange_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMaxAverageGrade(-0.1)); - assertThrows(IllegalArgumentException.class, - () -> generator.setMaxAverageGrade(5.1)); - } - @Test - void shouldTestsetMinMaxAverageGrade_minGreaterThanMax_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinAverageGrade(4.0).setMaxAverageGrade(3.0).generate(1)); - } - @Test - void shouldTestsetMinRecordBookNumber_lessThanAllowed_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinRecordBookNumber(0)); - } - @Test - void shouldTestsetMinMaxRecordBookNumber_minGreaterThanMax_throwsException() { - RandomStudentGenerator generator = new RandomStudentGenerator(); - assertThrows(IllegalArgumentException.class, - () -> generator.setMinRecordBookNumber(20_000).setMaxRecordBookNumber(10_000).generate(1)); - } - @Test - void shouldTestchainedBuilderStyle_worksAsExpected() { - RandomStudentGenerator generator = new RandomStudentGenerator() - .setMinGroupNumber("A00") - .setMaxGroupNumber("Z99") - .setMinAverageGrade(2.0) - .setMaxAverageGrade(4.0) - .setMinRecordBookNumber(1_000) - .setMaxRecordBookNumber(9_999); - MyList students = generator.generate(50); - assertEquals(50, students.size()); - for (int i = 0; i < students.size(); i++) { - Student s = students.get(i); - assertTrue(s.getGroupNumber().compareTo("A00") >= 0 - && s.getGroupNumber().compareTo("Z99") <= 0); - assertTrue(s.getAverageGrade() >= 2.0 && s.getAverageGrade() <= 4.0); - assertTrue(s.getRecordBookNumber() >= 1_000 && s.getRecordBookNumber() <= 9_999); - } + } + + @Test + void shouldTestgenerate_negativeSize_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows(IllegalArgumentException.class, () -> generator.generate(-1)); + } + + @Test + void shouldTestsetMinGroupNumber_invalidPattern_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows(IllegalArgumentException.class, () -> generator.setMinGroupNumber("invalid")); + } + + @Test + void shouldTestsetMaxGroupNumber_invalidPattern_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows( + IllegalArgumentException.class, () -> generator.setMaxGroupNumber("ZZ")); // не 2 цифры + } + + @Test + void shouldTestsetMinMaxGroupNumber_minGreaterThanMax_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows( + IllegalArgumentException.class, + () -> generator.setMinGroupNumber("C00").setMaxGroupNumber("A99").generate(1)); + } + + @Test + void shouldTestsetMinAverageGrade_outOfRange_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows(IllegalArgumentException.class, () -> generator.setMinAverageGrade(-0.1)); + assertThrows(IllegalArgumentException.class, () -> generator.setMinAverageGrade(5.1)); + } + + @Test + void shouldTestsetMaxAverageGrade_outOfRange_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows(IllegalArgumentException.class, () -> generator.setMaxAverageGrade(-0.1)); + assertThrows(IllegalArgumentException.class, () -> generator.setMaxAverageGrade(5.1)); + } + + @Test + void shouldTestsetMinMaxAverageGrade_minGreaterThanMax_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows( + IllegalArgumentException.class, + () -> generator.setMinAverageGrade(4.0).setMaxAverageGrade(3.0).generate(1)); + } + + @Test + void shouldTestsetMinRecordBookNumber_lessThanAllowed_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows(IllegalArgumentException.class, () -> generator.setMinRecordBookNumber(0)); + } + + @Test + void shouldTestsetMinMaxRecordBookNumber_minGreaterThanMax_throwsException() { + RandomStudentGenerator generator = new RandomStudentGenerator(); + assertThrows( + IllegalArgumentException.class, + () -> generator.setMinRecordBookNumber(20_000).setMaxRecordBookNumber(10_000).generate(1)); + } + + @Test + void shouldTestchainedBuilderStyle_worksAsExpected() { + RandomStudentGenerator generator = + new RandomStudentGenerator() + .setMinGroupNumber("A00") + .setMaxGroupNumber("Z99") + .setMinAverageGrade(2.0) + .setMaxAverageGrade(4.0) + .setMinRecordBookNumber(1_000) + .setMaxRecordBookNumber(9_999); + MyList students = generator.generate(50); + assertEquals(50, students.size()); + for (int i = 0; i < students.size(); i++) { + Student s = students.get(i); + assertTrue( + s.getGroupNumber().compareTo("A00") >= 0 && s.getGroupNumber().compareTo("Z99") <= 0); + assertTrue(s.getAverageGrade() >= 2.0 && s.getAverageGrade() <= 4.0); + assertTrue(s.getRecordBookNumber() >= 1_000 && s.getRecordBookNumber() <= 9_999); } + } } diff --git a/app/src/test/java/ru/project/search/StudentSearchTest.java b/app/src/test/java/ru/project/search/StudentSearchTest.java index 9ef14ef..2b9b756 100644 --- a/app/src/test/java/ru/project/search/StudentSearchTest.java +++ b/app/src/test/java/ru/project/search/StudentSearchTest.java @@ -3,8 +3,8 @@ package ru.project.search; import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; -import ru.project.collection.MyList; import ru.project.collection.MyLinkedList; +import ru.project.collection.MyList; import ru.project.student.Student; class StudentSearchTest { @@ -79,7 +79,7 @@ class StudentSearchTest { @Test void testAverageGradeRangeDoesNotMatchAbove() { StudentSearchCriteria criteria = new AverageGradeRangeCriteria(4.0, 5.0); - Student student = new Student("A12", 5.1, 123); + Student student = new Student("A12", 2.0, 123); assertFalse(criteria.matches(student)); } @@ -199,8 +199,10 @@ class StudentSearchTest { Student student = new Student("B20", 4.5, 150); assertTrue(outer.matches(student)); + student = new Student("B20", 3.5, 150); - assertFalse(outer.matches(student)); + assertTrue(outer.matches(student)); + student = new Student("D00", 4.5, 150); assertFalse(outer.matches(student)); } diff --git a/app/src/test/java/ru/project/sorting/SortStrategyTest.java b/app/src/test/java/ru/project/sorting/SortStrategyTest.java index 7a6724c..36db74c 100644 --- a/app/src/test/java/ru/project/sorting/SortStrategyTest.java +++ b/app/src/test/java/ru/project/sorting/SortStrategyTest.java @@ -78,8 +78,9 @@ class SortStrategyTest { list.add(new Student("A05", 4.5, 5)); list.add(new Student("A06", 4.5, 4)); - EvenOnlyMergeSortStrategy strategy = - new EvenOnlyMergeSortStrategy<>(Student::getRecordBookNumber); + EvenOnlySortStrategy strategy = + new EvenOnlySortStrategy<>(new MergeSortStrategy(), Student::getRecordBookNumber); + strategy.sort(list, Comparator.comparingInt(Student::getRecordBookNumber)); int[] expected = {7, 2, 3, 4, 5, 8}; @@ -95,8 +96,8 @@ class SortStrategyTest { list.add(new Student("A02", 4.5, 3)); list.add(new Student("A03", 4.5, 5)); - EvenOnlyMergeSortStrategy strategy = - new EvenOnlyMergeSortStrategy<>(Student::getRecordBookNumber); + EvenOnlySortStrategy strategy = + new EvenOnlySortStrategy<>(new MergeSortStrategy(), Student::getRecordBookNumber); strategy.sort(list, Comparator.comparingInt(Student::getRecordBookNumber)); int[] expected = {1, 3, 5}; @@ -112,8 +113,8 @@ class SortStrategyTest { list.add(new Student("A02", 4.5, 2)); list.add(new Student("A03", 4.5, 6)); - EvenOnlyMergeSortStrategy strategy = - new EvenOnlyMergeSortStrategy<>(Student::getRecordBookNumber); + EvenOnlySortStrategy strategy = + new EvenOnlySortStrategy<>(new MergeSortStrategy(), Student::getRecordBookNumber); strategy.sort(list, Comparator.comparingInt(Student::getRecordBookNumber)); int[] expected = {2, 4, 6};