304 lines
8.7 KiB
Java
304 lines
8.7 KiB
Java
//CipherStreamJava/src/main/java/com/mattrixwv/cipherstream/monosubstitution/Caesar.java
|
|
//Matthew Ellison
|
|
// Created: 07-25-21
|
|
//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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.mattrixwv.cipherstream.monosubstitution;
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import com.mattrixwv.cipherstream.exceptions.InvalidInputException;
|
|
|
|
|
|
/**
|
|
* A class for encoding and decoding strings using the Caesar cipher.
|
|
*
|
|
* <p>
|
|
* The Caesar cipher is a substitution cipher where each letter in the
|
|
* plaintext is shifted a fixed number of places down or up the alphabet.
|
|
* This class allows you to encode and decode strings with options to preserve
|
|
* capitalization, whitespace, and symbols.
|
|
* </p>
|
|
*/
|
|
public class Caesar{
|
|
private static final Logger logger = LoggerFactory.getLogger(Caesar.class);
|
|
//?Fields
|
|
/** The string that needs encoded/decoded */
|
|
protected String inputString;
|
|
/** The encoded/decoded string */
|
|
protected String outputString;
|
|
/** The amount that you need to shift each letter */
|
|
protected int shift;
|
|
//?Settings
|
|
/** Persist capitals in the output string */
|
|
protected boolean preserveCapitals;
|
|
/** Persist whitespace in the output string */
|
|
protected boolean preserveWhitespace;
|
|
/** Persist symbols in the output string */
|
|
protected boolean preserveSymbols;
|
|
|
|
|
|
/**
|
|
* Sets the shift amount and ensures it is within the proper bounds (0-25).
|
|
*
|
|
* @param shiftAmount the amount to shift each letter
|
|
*/
|
|
protected void setShift(int shiftAmount){
|
|
logger.debug("Setting shift {}", shiftAmount);
|
|
|
|
//If you shift more than 26 you will just be wrapping back around again
|
|
shift = shiftAmount % 26;
|
|
|
|
logger.debug("Cleaned shift {}", shift);
|
|
}
|
|
/**
|
|
* Sets the input string for encoding or decoding, applying removal options
|
|
* for case, whitespace, and symbols.
|
|
*
|
|
* @param inputString the string to be processed
|
|
* @throws InvalidInputException if the input string is null or blank after processing
|
|
*/
|
|
protected void setInputString(String inputString) throws InvalidInputException{
|
|
if(inputString == null){
|
|
throw new InvalidInputException("Input cannot be null");
|
|
}
|
|
|
|
logger.debug("Original input string '{}'", inputString);
|
|
|
|
if(!preserveCapitals){
|
|
logger.debug("Removing case");
|
|
|
|
inputString = inputString.toLowerCase();
|
|
}
|
|
if(!preserveWhitespace){
|
|
logger.debug("Removing whitespace");
|
|
|
|
inputString = inputString.replaceAll("\\s", "");
|
|
}
|
|
if(!preserveSymbols){
|
|
logger.debug("Removing symbols");
|
|
|
|
inputString = inputString.replaceAll("[^a-zA-Z\\s]", "");
|
|
}
|
|
|
|
logger.debug("Cleaned input string '{}'", inputString);
|
|
this.inputString = inputString;
|
|
|
|
if(this.inputString.isBlank()){
|
|
throw new InvalidInputException("Input must contain at least 1 letter");
|
|
}
|
|
}
|
|
/**
|
|
* Encodes the input string by shifting letters according to the Caesar cipher.
|
|
*/
|
|
protected void encode(){
|
|
logger.debug("Encoding");
|
|
|
|
StringBuilder output = new StringBuilder();
|
|
for(int cnt = 0;cnt < inputString.length();++cnt){
|
|
char currentChar = inputString.charAt(cnt); //A temperary holder for the current working character
|
|
logger.debug("Working character {}", currentChar);
|
|
|
|
//If it is an upper case letter shift it and wrap if necessary
|
|
if(Character.isUpperCase(currentChar)){
|
|
logger.debug("Encoding uppercase");
|
|
|
|
currentChar += shift;
|
|
//Wrap around if the letter is now out of bounds
|
|
if(currentChar < 'A'){
|
|
logger.debug("Wrapping around to Z");
|
|
currentChar += 26;
|
|
}
|
|
else if(currentChar > 'Z'){
|
|
logger.debug("Wrapping around to A");
|
|
currentChar -= 26;
|
|
}
|
|
}
|
|
//If it is a lower case letter shift it and wrap if necessary
|
|
else if(Character.isLowerCase(currentChar)){
|
|
logger.debug("Encoding lowercase");
|
|
|
|
currentChar += shift;
|
|
//Wrap around if the letter is now out of bounds
|
|
if(currentChar < 'a'){
|
|
logger.debug("Wrapping around to z");
|
|
currentChar += 26;
|
|
}
|
|
else if(currentChar > 'z'){
|
|
logger.debug("Wrapping around to a");
|
|
currentChar -= 26;
|
|
}
|
|
}
|
|
//If it is whitespace, number, or punctuation just let it pass through
|
|
//Add it to the output string
|
|
logger.debug("Encoded character {}", currentChar);
|
|
output.append(currentChar);
|
|
}
|
|
|
|
outputString = output.toString();
|
|
logger.debug("Saving encoded string '{}'", outputString);
|
|
}
|
|
/**
|
|
* Decodes the input string by reversing the shift applied during encoding.
|
|
*/
|
|
protected void decode(){
|
|
logger.debug("Decoding");
|
|
|
|
StringBuilder output = new StringBuilder();
|
|
for(int cnt = 0;cnt < inputString.length();++cnt){
|
|
char currentChar = inputString.charAt(cnt); //A temperary holder for the current working character
|
|
logger.debug("Working character {}", currentChar);
|
|
|
|
//If it is an upper case letter shift it and wrap if necessary
|
|
if(Character.isUpperCase(currentChar)){
|
|
logger.debug("Decoding uppercase");
|
|
|
|
currentChar -= shift;
|
|
//Wrap around if the letter is now out of bounds
|
|
if(currentChar < 'A'){
|
|
logger.debug("Wrapping around to Z");
|
|
|
|
currentChar += 26;
|
|
}
|
|
else if(currentChar > 'Z'){
|
|
logger.debug("Wrapping around to A");
|
|
|
|
currentChar -= 26;
|
|
}
|
|
}
|
|
//If it is a lower case letter shift it and wrap if necessary
|
|
else if(Character.isLowerCase(currentChar)){
|
|
logger.debug("Decoding lowercase");
|
|
|
|
currentChar -= shift;
|
|
//Wrap around if the letter is now out of bounds
|
|
if(currentChar < 'a'){
|
|
logger.debug("Wrapping around to z");
|
|
|
|
currentChar += 26;
|
|
}
|
|
else if(currentChar > 'z'){
|
|
logger.debug("Wrapping around to a");
|
|
|
|
currentChar -= 26;
|
|
}
|
|
}
|
|
//If it is whitespace, number, or punctuation just let it pass through
|
|
//Add it to the output string
|
|
logger.debug("Decoded character {}", currentChar);
|
|
output.append(currentChar);
|
|
}
|
|
|
|
outputString = output.toString();
|
|
logger.debug("Saving decoded string '{}'", outputString);
|
|
}
|
|
|
|
//?Constructor
|
|
/**
|
|
* Constructs a new {@code Caesar} instance with default settings.
|
|
*/
|
|
public Caesar(){
|
|
reset();
|
|
preserveCapitals = false;
|
|
preserveWhitespace = false;
|
|
preserveSymbols = false;
|
|
}
|
|
/**
|
|
* Constructs a new {@code Caesar} instance with specified settings.
|
|
*
|
|
* @param preserveCapitals whether to preserve capitalization in the output
|
|
* @param preserveWhitespace whether to preserve whitespace in the output
|
|
* @param preserveSymbols whether to preserve symbols in the output
|
|
*/
|
|
public Caesar(boolean preserveCapitals, boolean preserveWhitespace, boolean preserveSymbols){
|
|
reset();
|
|
this.preserveCapitals = preserveCapitals;
|
|
this.preserveWhitespace = preserveWhitespace;
|
|
this.preserveSymbols = preserveSymbols;
|
|
}
|
|
|
|
/**
|
|
* Encodes the input string with the specified shift amount.
|
|
*
|
|
* @param shiftAmount the amount to shift each letter
|
|
* @param inputString the string to be encoded
|
|
* @return the encoded string
|
|
* @throws InvalidInputException if the input string is invalid
|
|
*/
|
|
public String encode(int shiftAmount, String inputString) throws InvalidInputException{
|
|
reset();
|
|
setShift(shiftAmount);
|
|
setInputString(inputString);
|
|
encode();
|
|
return outputString;
|
|
}
|
|
/**
|
|
* Decodes the input string with the specified shift amount.
|
|
*
|
|
* @param shiftAmount the amount to shift each letter
|
|
* @param inputString the string to be decoded
|
|
* @return the decoded string
|
|
* @throws InvalidInputException if the input string is invalid
|
|
*/
|
|
public String decode(int shiftAmount, String inputString) throws InvalidInputException{
|
|
reset();
|
|
setShift(shiftAmount);
|
|
setInputString(inputString);
|
|
decode();
|
|
return outputString;
|
|
}
|
|
|
|
//?Getters
|
|
/**
|
|
* Gets the current input string.
|
|
*
|
|
* @return the input string
|
|
*/
|
|
public String getInputString(){
|
|
return inputString;
|
|
}
|
|
/**
|
|
* Gets the current shift amount.
|
|
*
|
|
* @return the shift amount
|
|
*/
|
|
public int getShift(){
|
|
return shift;
|
|
}
|
|
/**
|
|
* Gets the current output string.
|
|
*
|
|
* @return the output string
|
|
*/
|
|
public String getOutputString(){
|
|
return outputString;
|
|
}
|
|
/**
|
|
* Resets the internal fields to their default values.
|
|
*/
|
|
public void reset(){
|
|
logger.debug("Resetting fields");
|
|
|
|
inputString = "";
|
|
outputString = "";
|
|
shift = 0;
|
|
}
|
|
}
|