//CipherStreamJava/src/main/java/com/mattrixwv/cipherstream/polysubstitution/LargePolybiusSquare.java //Mattrixwv // Created: 04-21-23 // Modified: 08-11-24 /* Copyright (C) 2024 Mattrixwv This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ package com.mattrixwv.cipherstream.polysubstitution; import java.util.StringJoiner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.mattrixwv.cipherstream.exceptions.InvalidCharacterException; import com.mattrixwv.cipherstream.exceptions.InvalidInputException; import com.mattrixwv.cipherstream.exceptions.InvalidKeywordException; /** * Represents the Polybius square cipher encryption and decryption. * The Polybius square cipher is a classical encryption method that uses a 6x6 grid * to encode and decode messages based on the positions of letters and numbers in the grid. */ public final class LargePolybiusSquare extends PolybiusSquare{ private static final Logger logger = LoggerFactory.getLogger(LargePolybiusSquare.class); /** * Creates the grid from the keyword. */ @Override protected void createGrid(){ logger.debug("Creating grid"); for(int row = 0;row < 6;++row){ for(int col = 0;col < 6;++col){ char letter = keyword.charAt((6 * row) + col); grid[row][col] = letter; } } } /** * Strips invalid characters from the string that needs encoding/decoding. * * @param inputString the input string to be cleaned * @throws InvalidCharacterException if an invalid character is found * @throws InvalidInputException if the input string is invalid */ @Override protected void setInputStringEncode(String inputString) throws InvalidCharacterException, InvalidInputException{ if(inputString == null){ throw new InvalidInputException("Input cannot be null"); } logger.debug("Original input string '{}'", inputString); //Change to upper case inputString = inputString.toUpperCase(); //Remove any whitespace if selected if(!preserveWhitespace){ logger.debug("Removing whitespace"); inputString = inputString.replaceAll("\\s", ""); } //Remove any symbols if selected if(!preserveSymbols){ logger.debug("Removing symbols"); inputString = inputString.replaceAll("[^a-zA-Z0-9\\s]", ""); } if(!preserveWhitespace && !preserveSymbols){ //Add whitespace after every character for the default look StringJoiner spacedString = new StringJoiner(" "); for(int cnt = 0;cnt < inputString.length();++cnt){ spacedString.add(Character.toString(inputString.charAt(cnt))); } inputString = spacedString.toString(); } //Save the string this.inputString = inputString; logger.debug("Cleaned input string '{}'", inputString); if(this.inputString.isBlank() || getPreparedInputStringEncode().isBlank()){ throw new InvalidInputException("Input must contain at least 1 letter"); } } /** * Returns the input string ready for encoding. * * @return the prepared input string */ @Override protected String getPreparedInputStringEncode(){ logger.debug("Preparing input string for encoding"); String cleanString = inputString.toUpperCase(); cleanString = cleanString.replaceAll("[^A-Z0-9]", ""); logger.debug("Prepared input string '{}'", cleanString); return cleanString; } /** * Strips invalid characters from the keyword and creates the grid. * * @param keyword the keyword to be processed */ @Override protected void setKeyword(String keyword) throws InvalidKeywordException{ if(keyword == null){ throw new InvalidKeywordException("Keyword cannot be null"); } logger.debug("Original keyword '{}'", keyword); //Change everything to uppercase keyword = keyword.toUpperCase(); //Remove everything except capital letters and numbers keyword = keyword.replaceAll("[^A-Z0-9]", ""); //Add all letters in the alphabet to the key keyword += "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; //Remove all duplicate characters StringBuilder uniqueKey = new StringBuilder(); keyword.chars().distinct().forEach(c -> uniqueKey.append((char)c)); keyword = uniqueKey.toString(); logger.debug("Cleaned keyword '{}'", keyword); this.keyword = keyword; //Create the grid from the sanitized keyword createGrid(); } /** * Adds characters that aren't letters to the output during encoding. * * @param cleanString the cleaned string to be formatted */ @Override protected void addCharactersToCleanStringEncode(String cleanString){ logger.debug("Formatting output string"); int outputCnt = 0; StringBuilder fullOutput = new StringBuilder(); for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){ logger.debug("Current character {}", inputString.charAt(inputCnt)); //Add both numbers of any letters to the output if(Character.isAlphabetic(inputString.charAt(inputCnt)) || Character.isDigit(inputString.charAt(inputCnt))){ logger.debug("Appending character"); fullOutput.append(cleanString.charAt(outputCnt++)); fullOutput.append(cleanString.charAt(outputCnt++)); } //Add any other characters that appear to the output else{ logger.debug("Appending symbol"); fullOutput.append(inputString.charAt(inputCnt)); } } outputString = fullOutput.toString(); logger.debug("Saving output string {}", outputString); } /** * Adds characters that aren't letters to the output during decoding. * * @param cleanString the cleaned string to be formatted */ @Override protected void addCharactersToCleanStringDecode(String cleanString){ logger.debug("Formatting output string"); int outputCnt = 0; StringBuilder fullOutput = new StringBuilder(); for(int inputCnt = 0;inputCnt < inputString.length();++inputCnt){ logger.debug("Current character {}", inputString.charAt(inputCnt)); //Add the letter to the output and skip the second number if(Character.isDigit(inputString.charAt(inputCnt)) || Character.isAlphabetic(inputString.charAt(inputCnt))){ logger.debug("Appending character"); fullOutput.append(cleanString.charAt(outputCnt++)); ++inputCnt; } //Add any other characters that appear to the output else{ logger.debug("Appending symbol"); fullOutput.append(inputString.charAt(inputCnt)); } } outputString = fullOutput.toString(); logger.debug("Saving output string {}", outputString); } /** * Resets all variables to their default values. */ @Override public void reset(){ logger.debug("Resetting"); grid = new char[6][6]; inputString = ""; outputString = ""; keyword = ""; } //?Constructors /** * Constructs a PolybiusSquare cipher instance with default settings. * * @throws InvalidCharacterException if default characters are invalid */ public LargePolybiusSquare() throws InvalidCharacterException{ super(); reset(); } /** * Constructs a PolybiusSquare cipher instance with specified settings. * * @param preserveWhitespace whether to preserve whitespace * @param preserveSymbols whether to preserve symbols * @throws InvalidCharacterException if default characters are invalid */ public LargePolybiusSquare(boolean preserveWhitespace, boolean preserveSymbols) throws InvalidCharacterException{ super(preserveWhitespace, preserveSymbols); reset(); } }