Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/main/java/com/javarush/golikov/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.javarush.golikov;

import com.javarush.golikov.controller.Controller;
import com.javarush.golikov.entite.Result;
import com.javarush.golikov.view.ShowMenu;

public class Application {
private final Controller controller = new Controller();
private final ShowMenu showMenu = new ShowMenu();

public Result run() {
int menuIndex = showMenu.getMenuItem();
String[] parametrises = showMenu.getParameterises(menuIndex == 0 || menuIndex == 1);
return controller.doAction(menuIndex, parametrises);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Условие menuIndex == 0 || menuIndex == 1 является 'хрупким'. Если порядок в меню изменится, логика сломается. Используйте полиморфизм для определения необходимости ключа.

}
}
11 changes: 11 additions & 0 deletions src/main/java/com/javarush/golikov/ConsoleRunner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.javarush.golikov;

import com.javarush.golikov.entite.Result;

public class ConsoleRunner {
public static void main(String[] args) {
Application application = new Application();
Result result = application.run();
System.out.println(result);
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/javarush/golikov/constatnts/Constants.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.javarush.golikov.constatnts;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Опечатка в названии пакета: constatnts вместо constants. Внимательность к деталям в именовании — признак профессионализма.


import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Constants {
private static final String ruAlphabet = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ";
private static final String specialCharacters = "\n.,\":-!? ";

public static final char[] ALPHABET = (ruAlphabet.toLowerCase() + specialCharacters).toCharArray();
public static final String DEFAULT_FOLDER = System.getProperty("user.dir") + File.separator + "text" + File.separator;

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поле ALPHABET_LIST является публичным и изменяемым (ArrayList). Сделайте его private static final и используйте List.of(), чтобы защитить данные от изменений.

public static final List<Character> ALPHABET_LIST = new ArrayList<>();

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вместо ручного заполнения списка в static-блоке используйте Stream API: ruAlphabet.toLowerCase().chars().mapToObj(c -> (char)c).toList(). Это короче и использует современные возможности Java.

static {
for(Character simbol : ALPHABET) ALPHABET_LIST.add(simbol);
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/javarush/golikov/controller/Actions.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.javarush.golikov.controller;

import com.javarush.golikov.exception.ApplicationExceptions;
import com.javarush.golikov.menuComands.BruteForce;
import com.javarush.golikov.menuComands.MenuCommands;
import com.javarush.golikov.menuComands.Decoding;
import com.javarush.golikov.menuComands.Encryption;

import java.util.HashMap;
import java.util.Map;

public class Actions {

private final Map<Integer, MenuCommands> actionMap = new HashMap<>();

public Actions() {
actionMap.put(0, new Encryption());
actionMap.put(1, new Decoding());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Используйте Map.of(0, new Encryption(), ...) для создания неизменяемой карты. Это более современный и лаконичный способ инициализации коллекций в Java.

actionMap.put(2, new BruteForce());
}

public MenuCommands findAction(int actionIndex) {
if (actionMap.containsKey(actionIndex)) {
try {
return actionMap.get(actionIndex);
} catch (IllegalArgumentException e) {
throw new ApplicationExceptions("Пункт меню не реализован!", e);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Метод actionMap.get() не выбрасывает IllegalArgumentException. Блок try-catch здесь бесполезен. Достаточно проверки на null или containsKey.

} else throw new ApplicationExceptions("Пункт меню не реализован!");
}
}
18 changes: 18 additions & 0 deletions src/main/java/com/javarush/golikov/controller/Controller.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.javarush.golikov.controller;

import com.javarush.golikov.entite.Result;
import com.javarush.golikov.exception.ApplicationExceptions;
import com.javarush.golikov.menuComands.MenuCommands;


public class Controller {
public Result doAction(int actionNumber, String[] parametrises) {
Actions actions = new Actions();
try{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Объект Actions создается при каждом вызове метода. Сделайте его константой или полем класса, чтобы избежать лишних аллокаций памяти.

MenuCommands menuCommands = actions.findAction(actionNumber);
return menuCommands.execute(parametrises);
} catch (IllegalArgumentException | ApplicationExceptions e) {
return new Result(e.getMessage(), "Error");
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/com/javarush/golikov/entite/Result.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.javarush.golikov.entite;

public record Result(String message, String resultCode) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Record автоматически генерирует toString. Переопределяйте его только если вам нужен специфический формат вывода, отличный от стандартного.


@Override
public String toString() {
return "Result{" +
"message='" + message + '\'' +
", resultCode='" + resultCode + '\'' +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.javarush.golikov.exception;

public class ApplicationExceptions extends RuntimeException {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Название класса во множественном числе (Exceptions) некорректно. Следуйте Java Naming Convention: ApplicationException.


public ApplicationExceptions(String message) {
super(message);
}

public ApplicationExceptions(String message, Throwable cause) {
super(message, cause);
}

}
56 changes: 56 additions & 0 deletions src/main/java/com/javarush/golikov/menuComands/BruteForce.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.javarush.golikov.menuComands;

import com.javarush.golikov.constatnts.Constants;
import com.javarush.golikov.entite.Result;
import com.javarush.golikov.exception.ApplicationExceptions;

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class BruteForce extends Commands {
@Override
public Result execute(String[] parametrises) {
String source = parametrises[0];
if (Files.isDirectory(Path.of(source))) {
source += "output.txt";
}
String target = parametrises[1];
if (Files.isDirectory(Path.of(target))) {
target += "bfresult.txt";
}
int bestKey = 0;
int bestSpaceCount = 0;
char space = ' ';
for (int key = 0; key < Constants.ALPHABET.length; key++) {
int spaceCount = countCharInFileWithKey(source, key, space);
if (spaceCount > bestSpaceCount) {
bestSpaceCount = spaceCount;
bestKey = key;
}
}
return doCommand(source, target, bestKey);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поиск лучшего ключа можно реализовать через Stream API: IntStream.range(0, max).boxed().max(Comparator.comparingInt(...)). Это более декларативно.


private int countCharInFileWithKey(String encryptedFilename, int key, char fixChar) {
int spaceCount = 0;
Path path = Path.of(encryptedFilename);
try (BufferedReader reader = Files.newBufferedReader(path)) {
int value;
while ((value = reader.read()) > -1) {
char character = (char) value;
if (Constants.ALPHABET_LIST.contains(character)) {
int index = Constants.ALPHABET_LIST.indexOf(character);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вызов Files.newBufferedReader внутри цикла приводит к многократному открытию файла. Прочитайте содержимое файла один раз перед циклом для повышения производительности.

index = (index + key + Constants.ALPHABET.length) % Constants.ALPHABET.length;
if (Constants.ALPHABET[index] == fixChar) {
spaceCount++;
}
}
}
} catch (IOException e) {
throw new ApplicationExceptions("Файл не найден" + encryptedFilename, e);
}
return spaceCount;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/javarush/golikov/menuComands/Commands.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.javarush.golikov.menuComands;

import com.javarush.golikov.constatnts.Constants;
import com.javarush.golikov.entite.Result;
import com.javarush.golikov.exception.ApplicationExceptions;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public abstract class Commands implements MenuCommands {
public Result doCommand(String source, String target, int key) {
Path sourceFile = Path.of(source);
Path targetFile = Path.of(target);

try (BufferedReader fileReader = Files.newBufferedReader(sourceFile);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вместо конкатенации путей через +=, используйте Path.resolve() или Path.of(dir, filename). Это кроссплатформенный и безопасный способ работы с путями.

BufferedWriter fileWriter = Files.newBufferedWriter(targetFile)) {
int value;
int length = Constants.ALPHABET.length;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование fileReader.read() (посимвольное чтение) очень медленное. Лучше использовать Files.readAllLines или буферизированное чтение блоками.

while ((value = fileReader.read()) > -1) {
char character = (char) value;
character = Character.toLowerCase(character);
if (Constants.ALPHABET_LIST.contains(character)) {
int index = Constants.ALPHABET_LIST.indexOf(character);
index = (index + key + Math.abs(key) * length) % length;
fileWriter.write(Constants.ALPHABET[index]);
} else if (character == '\n') {
fileWriter.write(character);
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для вычисления индекса при отрицательных сдвигах используйте Math.floorMod(index + key, length). Это чище и надежнее, чем ручные манипуляции с Math.abs.

}
} catch (IOException e) {
throw new ApplicationExceptions("Файл не найден" + e.getMessage(), e);
}
return new Result(this.getClass().getSimpleName(), "Ok");
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/javarush/golikov/menuComands/Decoding.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.javarush.golikov.menuComands;

import com.javarush.golikov.entite.Result;
import com.javarush.golikov.exception.ApplicationExceptions;

import java.nio.file.Files;
import java.nio.file.Path;

public class Decoding extends Commands {

@Override
public Result execute(String[] parametrises) {
String source = parametrises[0];
if (Files.isDirectory(Path.of(source))) {
source += "output.txt";
}
String target = parametrises[1];
if (Files.isDirectory(Path.of(target))) {
target += "decoding.txt";
}
int key;
try {
key = Integer.parseInt(parametrises[2]);
} catch (NumberFormatException e) {
throw new ApplicationExceptions("Неверный ключ");
}
return doCommand(source, target, key * -1);
}
}
29 changes: 29 additions & 0 deletions src/main/java/com/javarush/golikov/menuComands/Encryption.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.javarush.golikov.menuComands;

import com.javarush.golikov.entite.Result;
import com.javarush.golikov.exception.ApplicationExceptions;

import java.nio.file.Files;
import java.nio.file.Path;

public class Encryption extends Commands {

@Override
public Result execute(String[] parametrises) {
String source = parametrises[0];
if (Files.isDirectory(Path.of(source))) {
source += "text.txt";
}
String target = parametrises[1];
if (Files.isDirectory(Path.of(target))) {
target += "output.txt";
}
int key;
try{
key = Integer.parseInt(parametrises[2]);
} catch(NumberFormatException e){
throw new ApplicationExceptions("Неверный ключ");
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Дублирование логики парсинга ключа и проверки путей в Encryption и Decoding. Вынесите эту валидацию в базовый класс или отдельный Validator.

return doCommand(source, target, key);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.javarush.golikov.menuComands;

import com.javarush.golikov.entite.Result;

public interface MenuCommands {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Используйте sealed interface для MenuCommands. Это гарантирует, что список команд ограничен и известен заранее, что улучшает безопасность и дизайн приложения (Java 17+).

Result execute(String[] parametrises);
}
52 changes: 52 additions & 0 deletions src/main/java/com/javarush/golikov/view/ShowMenu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.javarush.golikov.view;

import com.javarush.golikov.constatnts.Constants;

import java.util.Scanner;


public class ShowMenu {
private final Scanner scanner = new Scanner(System.in);

public int getMenuItem() {
int item;
do {
System.out.println("Выберите пункт меню\n" +
"""
1. Шифрование
2. Дешифровка
3. "Brute force"
4. Анализ
5. Выход
""" );
String input = scanner.nextLine();
item = switch (input) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование switch как выражения — это отлично, но возвращаемые магические числа (0, 1, 2) лучше заменить на Enum (ActionType).

case "1" -> 0;
case "2" -> 1;
case "3" -> 2;
case "4" -> 3;
case "5" -> 4;
default -> {
System.out.println("Неверный ввод. Повторите ввод");
yield -1;
}
};
} while (item < 0);
return item;
}

public String[] getParameterises(boolean isKeyNeeded) {
String[] parameterises = new String[3];
if (isKeyNeeded){
System.out.println("Введите ключ шифрования/дешифрования");
parameterises[2] = scanner.nextLine();
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование System.in напрямую через Scanner в ShowMenu затрудняет юнит-тестирование. Передавайте Scanner или InputStream через конструктор.

System.out.println("Введите путь исходного файла по умолчанию директория пользователя папка text");
String innFile = scanner.nextLine();
parameterises[0] = innFile.isEmpty() ? Constants.DEFAULT_FOLDER : innFile;
System.out.println("Введите путь конечного файла по умолчанию директория пользователя папка text");
String outFile = scanner.nextLine();
parameterises[1] = outFile.isEmpty() ? Constants.DEFAULT_FOLDER : innFile;
return parameterises;
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Логическая ошибка: переменной parameterises[1] присваивается innFile вместо outFile. Это приведет к тому, что результат всегда будет писаться в исходный файл.