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
12 changes: 12 additions & 0 deletions src/main/java/com/javarush/morozov/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.javarush.morozov;

import com.javarush.morozov.ui.MainWindow;

public class App {
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(() -> {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

import?

MainWindow mainWindow = new MainWindow();
mainWindow.setVisible(true);
});
}
}
159 changes: 159 additions & 0 deletions src/main/java/com/javarush/morozov/core/CaesarCipher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.javarush.morozov.core;

import com.javarush.morozov.ui.handlers.MessageHandler;

import java.util.*;

public class CaesarCipher {
public enum Language {
RUSSIAN("RUS", "Русские"),
ENGLISH("ENG", "Английские");

private final String code;
private final String displayName;

Language(String code, String displayName) {
this.code = code;
this.displayName = displayName;
}

public String getCode() {
return code;
}

public String getDisplayName() {
return displayName;
}

@Override
public String toString() {
return displayName;
}
}

private static final char[] RUSSIAN_ALPHABET = {
'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п',
'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я',
'.', ',', '«', '»', '"', '\'', ':', '!', '?', ' '
};

private static final char[] ENGLISH_ALPHABET = {
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'.', ',', '«', '»', '"', '\'', ':', '!', '?', ' '
};

private char[] currentAlphabet;
private Language currentLanguage;
private Set<Character> allowedChars;

public CaesarCipher(Language language) {
setLanguage(language);
}

public void setLanguage(Language language) {
this.currentLanguage = language;
this.currentAlphabet = language == Language.RUSSIAN ? RUSSIAN_ALPHABET : ENGLISH_ALPHABET;
this.allowedChars = new HashSet<>();

for (char c : currentAlphabet) {
allowedChars.add(c);
}
}

public String encrypt(String text, int key) throws IllegalArgumentException {
validateText(text);
return processText(text, key);
}

public String decrypt(String text, int key) throws IllegalArgumentException {
validateText(text);
return processText(text, -key);
}

private String processText(String text, int key) {
StringBuilder result = new StringBuilder();
text = text.toLowerCase();

for (char c : text.toCharArray()) {
int index = Arrays.binarySearch(currentAlphabet, c);

if (index >= 0) {
int newIndex = (index + key) % currentAlphabet.length;
if (newIndex < 0) newIndex += currentAlphabet.length;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Лучше было бы индекс сначала перевести в положительную область а уже потом вычислять остаток отделения, чтобы вся отладка была в положительных значениях, но это дело вкуса конечно.


result.append(currentAlphabet[newIndex]);
} else {
result.append(c);
}
}

return result.toString();
}

private void validateText(String text) throws IllegalArgumentException {
for (int i = 0; i < text.length(); i++) {
char c = Character.toLowerCase(text.charAt(i));

if (!allowedChars.contains(c)) {
String errorMessage = "Текст содержит недопустимые символы. Разрешены только "
+ currentLanguage.getDisplayName() + " символы и знаки пунктуации.";

MessageHandler.showErrorDialog(errorMessage);

throw new IllegalArgumentException(
String.format("Недопустимый символ '%c' в позиции %d", text.charAt(i), i)
);
}
}
}

/**
* Расшифровка методом BruteForce
* @param encryptedText расшифровываемый текст.
* @return Список результатов оценки метода BruteForce.
*/
public List<BruteForceResult> bruteForce(String encryptedText) {
List<BruteForceResult> results = new ArrayList<>();

for (int key = 1; key < currentAlphabet.length; key++) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

если ключ 0 (не зашифровано) то текст испортится.

String decrypted = processText(encryptedText, -key);
int score = calculateTextScore(decrypted);
results.add(new BruteForceResult(decrypted, key, score));
}

results.sort((a, b) -> Integer.compare(b.score, a.score));
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

тогда уже и все можно было бы стримом сделать


return results;
}

public record BruteForceResult(String text, int key, int score) {
}

private int calculateTextScore(String text) {
int score = 0;

long spaceCount = text.chars().filter(c -> c == ' ').count();
score += (int) (spaceCount * 10);

String vowels = currentLanguage == Language.RUSSIAN ? "аеёиоуыэюя" : "aeiouy";
long vowelCount = text.toLowerCase().chars()
.filter(c -> vowels.indexOf(c) >= 0)
.count();
score += (int) (vowelCount * 3);

String commonChars = currentLanguage == Language.RUSSIAN ? "оае" : "eta";
long commonCount = text.toLowerCase().chars()
.filter(c -> commonChars.indexOf(c) >= 0)
.count();
score += (int) (commonCount * 2);

String punctuation = ".,!?;:\"'";

if (text.matches(".*[a-zA-Zа-яА-Я][" + punctuation + "][a-zA-Zа-яА-Я].*")) {
score -= 20;
}

return score;
}
}
53 changes: 53 additions & 0 deletions src/main/java/com/javarush/morozov/core/FileHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.javarush.morozov.core;

import com.javarush.morozov.ui.handlers.MessageHandler;

import javax.swing.filechooser.FileNameExtensionFilter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class FileHandler {
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB

public String readTextFile(File file) throws IOException {
if (!file.exists()) {
throw new IOException("Файл не существует");
}

if (file.length() > MAX_FILE_SIZE) {
throw new IOException("Файл слишком большой (макс. " + MAX_FILE_SIZE/1024/1024 + " МБ)");
}

if (!file.canRead()) {
throw new IOException("Нет прав на чтение файла");
}

String content = Files.readString(Paths.get(file.getPath()));
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Если выделить три переменных то скорость не пострадает, а читать проще


if (content.trim().isEmpty()) {
throw new IOException("Файл пуст");
}

return content;
}

public void saveTextFile(File file, String content) throws IOException {
if (file.exists() && !file.canWrite()) {
throw new IOException("Нет прав на запись в файл");
}

if (file.exists()) {
if (!MessageHandler.confirmFileOverwrite()) {
throw new IOException("Сохранение отменено");
}
}

Files.writeString(file.toPath(), content);
}

public static FileNameExtensionFilter getTextFileFilter() {
return new FileNameExtensionFilter("Текстовые файлы (*.txt)", "txt");
}
}
98 changes: 98 additions & 0 deletions src/main/java/com/javarush/morozov/ui/MainWindow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.javarush.morozov.ui;

import com.javarush.morozov.core.CaesarCipher;
import com.javarush.morozov.core.FileHandler;
import com.javarush.morozov.ui.listeners.*;

import javax.swing.*;
import java.awt.*;

public class MainWindow extends JFrame {
private JTextArea inputTextArea;
private JTextArea outputTextArea;
private JButton encryptButton;
private JButton decryptButton;
private JButton bruteForceButton;
private JButton loadFileButton;
private JButton saveFileButton;
JComboBox<CaesarCipher.Language> languageComboBox;


public MainWindow() {
super("Шифр цезаря");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(800, 600);
setLayout(new BorderLayout()); // автоматическое управление расположением компонентов внутри контейнера

initUI();
setupListeners();
}

/**
* Инициализация UI заданными компонентами.
*/
private void initUI() {
JPanel topPanel = new JPanel(new BorderLayout());

JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
languageComboBox = new JComboBox<>(CaesarCipher.Language.values());
languageComboBox.setSelectedItem(CaesarCipher.Language.RUSSIAN);
headerPanel.add(new JLabel("Символы шифровки:"));
headerPanel.add(languageComboBox);
topPanel.add(headerPanel, BorderLayout.NORTH);

JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 10));
encryptButton = new JButton("Зашифровать");
decryptButton = new JButton("Расшифровать");
bruteForceButton = new JButton("Brute Force");
loadFileButton = new JButton("Загрузить файл");
saveFileButton = new JButton("Сохранить в файл");

buttonPanel.add(loadFileButton);
buttonPanel.add(encryptButton);
buttonPanel.add(decryptButton);
buttonPanel.add(bruteForceButton);
buttonPanel.add(saveFileButton);
topPanel.add(buttonPanel, BorderLayout.CENTER);

inputTextArea = new JTextArea();
outputTextArea = new JTextArea();
outputTextArea.setEditable(false);

JScrollPane inputScrollPane = new JScrollPane(inputTextArea);
JScrollPane outputScrollPane = new JScrollPane(outputTextArea);

JPanel textPanel = new JPanel(new GridLayout(1, 2, 10, 10));
textPanel.add(inputScrollPane);
textPanel.add(outputScrollPane);

add(topPanel, BorderLayout.NORTH);
add(textPanel, BorderLayout.CENTER);
}

/**
* Установка прослушиваний при нажатии на кнопки
*/
private void setupListeners() {
FileHandler handler = new FileHandler();
CaesarCipher cipher = new CaesarCipher(getSelectedLanguage());

LoadFileButtonListener loadListener = new LoadFileButtonListener(inputTextArea, outputTextArea, handler);
SaveFileButtonListener saveListener = new SaveFileButtonListener(outputTextArea, handler);
EncryptButtonListener encryptListener = new EncryptButtonListener(inputTextArea, outputTextArea, cipher);
DecryptButtonListener decryptListener = new DecryptButtonListener(inputTextArea, outputTextArea, cipher);
BruteForceButtonListener bruteForceListener = new BruteForceButtonListener(inputTextArea, outputTextArea, cipher);

loadFileButton.addActionListener(loadListener);
saveFileButton.addActionListener(saveListener);
encryptButton.addActionListener(encryptListener);
decryptButton.addActionListener(decryptListener);
bruteForceButton.addActionListener(bruteForceListener);
languageComboBox.addActionListener(_ -> cipher.setLanguage(getSelectedLanguage()));

}

private CaesarCipher.Language getSelectedLanguage() {
return (CaesarCipher.Language) languageComboBox.getSelectedItem();
}
}
32 changes: 32 additions & 0 deletions src/main/java/com/javarush/morozov/ui/handlers/MessageHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.javarush.morozov.ui.handlers;

import javax.swing.*;

public class MessageHandler {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Все методы статические, значит нужен приватный конструктор.

public static void showErrorDialog(String message) {
JOptionPane.showMessageDialog(
null,
message,
"Ошибка",
JOptionPane.ERROR_MESSAGE
);
}

public static void showSuccessDialog(String message) {
JOptionPane.showMessageDialog(
null,
message,
"Успех",
JOptionPane.INFORMATION_MESSAGE
);
}

public static boolean confirmFileOverwrite() {
return JOptionPane.showConfirmDialog(
null,
"Файл уже существует. Перезаписать?",
"Подтверждение",
JOptionPane.YES_NO_OPTION
) == JOptionPane.YES_OPTION;
}
}
Loading