Page 1 of 1

Secret Code V - The computer era Bits shift and rotate Rate Topic: -----

#1 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8334
  • View blog
  • Posts: 31,857
  • Joined: 06-March 08

Posted 24 September 2010 - 10:16 PM

With the invention of the digital computer the crypting/decrypting process took a completly different turn and this for 2 reasons:
- first we would encrypt numbers now not letters or symbols the whole message can be considered as a single big number
- second compouters can perform operations millions of times faster like any mechanical device like the enigma machine of the Germans durin Worl War II

In this tutorial we will play with bits and bits operations. Not to the RAS operations yet that will be in the next tutorial.

As in the other Secret Code tutorial 2 classes will be provided. One for console mode with a main() method that shows/test the encoding/decoding algorithms and a GUI version that will allow to dynamically show the encoding/decoding operations using the console class to perform the coding.

In the real world all the operations are performed on bits but it is very difficult to display them in a visual form so a class CharAndBits is provided to display all bits in bytes as character '0' and '1'. This class provides method to display at the console or in the GUI the binary representation of every byte.

This class will be used in the next tutorials.

The CharAndBits.java class to print/display bytes as bits fields


/**
 * A class for easy representation of all ASCII character on the console 
 * and on GUIs.
 * This class contains the binary representation of the char it represents
 * If the char is prinatble it returns its ASCII representation else it returns '.'
 */
public class CharAndBits implements Comparable<CharAndBits> {

	// the character by itself
	private char theChar;
	// the char to display (will be '.' if not printable
	private char toAscii;
	// it's int value used for bitwise operation
	private int intValue;
	// its 8 bits representation as a String
	private String binaryStr;
	// its 8 bits representation as 8 char containing '0' or '1'
	private char[] binaryChar;
	// its 8 bits representation as 8 int containing 0 or 1
	private int[] binaryInt;
	
	/**
	 * Constructor that receives the char as parameter
	 */
	public CharAndBits(char theChar) {
		// save it
		this.theChar = theChar;
		// test if it is printable if it is the case use it else use '.'
		if(isPrintable())
			toAscii = theChar;
		else
			toAscii = '.';
		// get it's int value
		intValue = theChar;
		intValue &= 0xFF;				// ok we just support the 255 Ascii characters
		// convert to binary char and int
		binaryChar = new char[8];
		binaryInt = new int[8];
		int temp = intValue;
		for(int i = 7; i >= 0; i--) {
			binaryInt[i] = temp & 1;
			binaryChar[i] = (char) binaryInt[i];
			binaryChar[i] += '0';
			temp >>>= 1;
		}
		// and the whole String
		binaryStr = new String(binaryChar);
	}
	
	/**
	 * return true of false depending if the char is printable (GUI or console)
	 */
	public boolean isPrintable() {
		return !Character.isISOControl(theChar);
	}
	
	/**
	 * Returns the binary representation of this char
	 */
	public String toBinaryString() {
		return binaryStr;
	}
	
	/**
	 * Get the printable version of this char
	 */
	public char getPrintableChar() {
		return toAscii;
	}
	
	/**
	 * Returns the int value of this char
	 */
	public int getIntValue() {
		return intValue;
	}
	
	/**
	 * Getter for the binary char 
	 */
	public char[] getBinaryChar() {
		return binaryChar;
	}
	/**
	 * To test 2 EasyCharacter for equality
	 */
	public boolean equals(CharAndBits other) {
		return compareTo(other) == 0;
	}
	/**
	 * Used to sort an array of EasyCharacter
	 */
	public int compareTo(CharAndBits other) {
		return intValue - other.intValue;
	}
	
	/**
	 * A static method to get an array of EasyCharacter from a String
	 * (so the caller does not have to perform the loop himself)
	 */
	public static CharAndBits[] newCharAndBitsArray(String str) {
		// if str is null return a 0 length array of EasyCharacter
		if(str == null)
			return new CharAndBits[0];
		// convert String received as parameter as an array of char
		char[] digit = str.toCharArray();
		CharAndBits[] array = new CharAndBits[digit.length];
		for(int i = 0; i < digit.length; i++)
			array[i] = new CharAndBits(digit[i]);
		return array;
	}
	/**
	 * A static method to get a printable String from an array of EasyCharacter[]
	 * to display at the console or in a GUI
	 */
	public static String getMsgString(CharAndBits[] array) {
		StringBuilder sb = new StringBuilder(array.length);
		// copy our EasyCharacter into the buffer
		for(CharAndBits ea : array) 
				sb.append(ea.toAscii);		// printable version
		// return the StringBuilder as a String
		return sb.toString();
	}
	
	/**
	 * To perform the Xor between 2 arrays of EasyCharcter
	 * and return an String with the 2 arrays XORed
	 * The first parameter is the message, the second the key
	 * The array returned will have the size of the message
	 * if the key is smaller than the message a wrapAround will occur
	 */
	public static String xorArray(CharAndBits[] msg, CharAndBits[] key) {
		// check for null or no length message
		if(msg == null || msg.length == 0)
			return "";								// return String of 0 length
		// create the digit to hold the xored value
		int msgLen = msg.length;
		StringBuilder sb = new StringBuilder(msgLen);
		
		// check for null or empty key in that case just return a copy of our message
		if(key == null || key.length == 0) {		
			for(CharAndBits ea : msg)
				sb.append(ea.theChar);
			return sb.toString();
		}
		// get length of the key and create new array
		int keyLen = key.length;
		// loop to perform the XOR between each element of the msg array and the key with wrap around
		for(int i = 0; i < msgLen; i++) {
			int val = msg[i].intValue ^ key[i % keyLen].intValue;
			sb.append((char) val);
		}
		// convert the StringBuilder array to a String
		return sb.toString();
	}
	
	/**
	 * static method that return a String representation in binary of an array of char
	 */
	public static String toBinaryString(char[] digit) {
		// use a StringBuilder to append the binary represenation
		StringBuilder sb = new StringBuilder(digit.length * 9);    // * 9 for the blank space
		for(char c : digit) {
			CharAndBits ea = new CharAndBits(c);
			// append the 01010101010
			sb.append(ea.toBinaryString());
			sb.append(' ');
		}
		// return as a String
		return sb.toString();
	}
}



Now the main class that can be run as a console application and will display the bits swap and rotation:

SwapAndRotate.java


import java.util.Scanner;
/**
 * Secret Code V
 * 
 * With the the invention on digital computers the coding/decoding took a completly new turn
 * for two reasons:
 * - first the processing of data could go thousand if not million of times faster than with
 *   mechanical coding machines like the Enigma machine
 * - second the encription could be performed on numbers rather than on letters, numbers and even symbols
 * 
 * Ok so what's so special with characters on a computer ?  It is because characters are represented by
 * a serie of bits.  (We will stay with plain Ascii for now for simplicity).
 * 
 * Imagine the message "Hello". The Ascii representation of Hello in 8 bits bytes is
 *    H        e        l        l        o
 * 01001000 01100101 01101100 01101100 01101111
 * 
 * Now we can imagine some simple encoding like:
 * - swap bit 7 and 0 of first letter
 * - swap bit 6 and 1 of second letter
 * - swap bit 5 and 2 of third letter
 * - ...
 * 
 * or we can imagine an algortihm that will be performed on the whole message like rotating
 * the whole messages bits by the number of characters in the message.
 * 
 * These will be simple substituation that ressembles a lot at the Vigenere square 
 * (Secret Code II - Tutorial) and can be decrypted the same way by frequency analysis
 *
 * This tutorial will explore quite quickly these 2 techniques. The next tutorial Secret Code VI
 * will explore how to use XOR operations and will approach more the RSA encryption algorithm.
 * 
 * We will have a class PlayingWithBits that will support 3 algorithms
 * - algorithm SWAP will swap 2 bits in each character of the message
 * - algotithm ROTATE will rotate the bits in the whole message by a certain number of bits
 * - algorithm BOTH will use both of the mechanisms
 * 
 * The key for algorithm SWAP is a String of even length containing pair of numbers betwwen 0 and 7
 * specifying which bits to swap in each character.  If the message is longer than the number
 * of pairs we will wrap around
 * Example of key: "263704"
 * - in the first character we will swap bits 2 and 6
 * - in the second character we will swap bits 3 and 7
 * - in the third character we will swap bits 0 and 4
 * - in the fourth character we start over again so swap bits 2 and 6
 * 
 * The key for algorithm ROTATE is a list of int separated by , that will specify by how many
 * bits we will rotate (left or right) the whole message depending of the length of the message. 
 * Again there will be a wrap around if the message contains more character than number of rotate
 * Example of key "4,-23,93,12"
 * - if the message has 1 character we will rotate the message by 4 bits
 * - if the message has 2 characters we will rotate the message by 23 bits to the left
 * - if the message has 3 characters we will rotate the message by 93 bits
 * - if the message has 4 characters we will rotate the message by 12 bits
 * - if the message has 5 characters wrap around and we will rotate the message by 4 bits
 * 
 */
public class SwapAndRotate {

	public static final int SWAP = 1, ROTATE = 2, BOTH = 3;
	// the keys used for encript/decript
    private int[] keySwap1, keySwap2;      // swap first with second
    private int[] keyRotate;
    
    /**
     *  Constructor that receives the keys as parameter
     */
	public SwapAndRotate(String swap, String rotate) {
		// call common method to set the initial keys or change it
		setKeySwap(swap);
		setKeyRotate(rotate);
	}
	
	/**
	 * Method to set the original SWAP key and permit to change it on the fly
	 * If the String is invalid we set the key to length 0 so no swap will occur)
	 */
	public void setKeySwap(String key) {
		// avoid invalid key
		if(!bValidateSwapKey(key)) {
			keySwap1 = new int[0];
			return;
		}
		// decompose te key as an array of digit
		int len = key.length();
		char[] digit = key.toCharArray();
		
		// so all digits are valid build our array
		keySwap1 = new int[len / 2];
		keySwap2 = new int[len / 2];
		int k = 0;      // index in both arrays
		for(int i = 0; i < len; i += 2) {
			// store and convert to int
			keySwap1[k] = digit[i] - '0';			// swap that bit
			keySwap2[k] = digit[i+1] - '0';			// with that one
			// the bit representation in the array are
			// char[0]-bit 7  char[1]-bit 6 ... char[7]-bit 0
			// so reverse them
			keySwap1[k] = 7 - keySwap1[k];
			keySwap2[k] = 7 - keySwap2[k];
			k++;
		}
	}
	
	/**
	 * A public static method to validate the Swap key
	 * It can be used by an application that the key is valid before
	 * creating a SwapAndRotate object or before changing the key
	 */
	public static boolean bValidateSwapKey(String key) {
		// avoid null key
		if(key == null || key.length() == 0) {
			return false;
		}
		// validate the key it must contain an even number of digits
		int len = key.length();
		if(len % 2 != 0) {
			return false;
		}
		// all the digits in the key must be between 0 and 7
		char[] digit = key.toCharArray();
		for(char c : digit) {
			if(c < '0' || c > '7') {		// if invalid char
				return false;
			}
		}
	
		// so all digits are valid
		return true;
	}
	/**
	 * To set the initial value or change the ROTATE key
	 * If the key string is invalid we will set the key length to 0 so no swp will occur 
	 */
	public void setKeyRotate(String key) {
		// avoid invalid key
		if(!bValidateRotateKey(key)) {
			keyRotate = new int[0];				// empty key
			return;
		}
		// separate between each ,
		String[] str = key.split(",");
		int len = str.length;
		// prepare the keyRotate array
		keyRotate = new int[len];
		// translate into int the Strings we know they are valid
		for(int i = 0; i < len; i++)
			keyRotate[i] = Integer.parseInt(str[i].trim());
	}
	
	/**
	 * A public static method to validate the Rotate key
	 * It can be used by an application that the key is valid before
	 * creating a SwapAndRotate object or before changing the key
	 */
	public static boolean bValidateRotateKey(String key) {
		// avoid null key
		if(key == null || key.length() == 0) {
			return false;
		}
		// separate between each ,
		String[] str = key.split(",");
		int len = str.length;
		// translate into int the String if one fail we set to 0 key and no translation will occur
		for(int i = 0; i < len; i++) {
			// try to convert next token
			try {
				Integer.parseInt(str[i].trim());
			}
			catch (NumberFormatException e) {
				return false;						// so the key is invalid
			}
		}
		// ok valid key
		return true;
	}
	/** 
	 * Method that encode a message based on the registered keys
	 * the second parameter is the type of encryption SWAP, ROTATE or BOTH
	 */
	public char[] encode(String clear, int type) {
		// validate that the message is not null or length == 0
		if(clear == null || clear.length() == 0)
			return new char[0];
		// validate the type operation and if the key(s) is/are valid for that operation
		switch(type) {
			case SWAP:
				// if no valid SWAP key return
				if(keySwap1.length == 0)
					return new char[0];
				break;

			case ROTATE:
				// if no valid ROTATE key return
				if(keyRotate.length == 0)
					return new char[0];
				break;

			case BOTH:
				// if any of the 2 keys not valid return
				if(keySwap1.length == 0 || keyRotate.length == 0)
					return new char[0];
				break;

			default:					// invalid type of encoding
				return new char[0];
		}
		// make an array of EasyCharacter from both the message
		CharAndBits[] ea = CharAndBits.newCharAndBitsArray(clear);
		
		// make the array of the char that will be returned
		char[] digit = new char[clear.length()];
		
		// perform the encoding
		// if SWAP or BOTH start by the SWAP
		if(type == SWAP || type == BOTH) {
			performSwap(ea, digit);
			// if BOTH rebuild the CharAndBits array
			if(type == BOTH) {
				for(int i = 0; i < ea.length; i++)
					ea[i] = new CharAndBits(digit[i]);
			}
		}
		
		// perform the rotate
		if(type == ROTATE || type == BOTH) {
			// determine wich rotate to use
			int index = clear.length() % keyRotate.length;
			int nbRotate = keyRotate[index];
			// no need to rotate the message more than once
			nbRotate %= ea.length;
			performRotate(ea, digit, nbRotate);
		}
		return digit;
	}
	
	/** 
	 * Method that encode a message based on the registered keys
	 * the second parameter is the type of encryption SWAP, ROTATE or BOTH
	 */
	public char[] decode(String coded, int type) {
		// validate that the message is not null or length == 0
		if(coded == null || coded.length() == 0)
			return new char[0];
		// validate the type operation and if the key(s) is/are valid for that operation
		switch(type) {
			case SWAP:
				// if no valid SWAP key return
				if(keySwap1.length == 0)
					return new char[0];
				break;

			case ROTATE:
				// if no valid ROTATE key return
				if(keyRotate.length == 0)
					return new char[0];
				break;

			case BOTH:
				// if any of the 2 keys not valid return
				if(keySwap1.length == 0 || keyRotate.length == 0)
					return new char[0];
				break;

			default:					// invalid type of encoding
				return new char[0];
		}
		// make an array of EasyCharacter from both the message
		CharAndBits[] ea = CharAndBits.newCharAndBitsArray(coded);
		
		// make the array of the char that will be returned
		char[] digit = new char[coded.length()];
		
		// perform the encoding
		// if ROTATE or BOTH start by the ROTATE
		// perform the rotate
		if(type == ROTATE || type == BOTH) {
			// determine wich rotate to use
			int index = coded.length() % keyRotate.length;
			int nbRotate = keyRotate[index];
			// no need to rotate the message more than once
			nbRotate %= ea.length;
			performRotate(ea, digit, -nbRotate);
			// if BOTH rebuild the CharAndBits array before performing the swap
			if(type == BOTH) {
				for(int i = 0; i < ea.length; i++)
					ea[i] = new CharAndBits(digit[i]);
			}
		}
		// test if SWAP
		if(type == SWAP || type == BOTH)
			performSwap(ea, digit);
		
		return digit;
	}
	
	/**
	 * Perform the encoding/decoding of the SWAP algorithm
	 * As only swapping is performed the same method can be used for
	 * encoding and decoding
	 */
	private void performSwap(CharAndBits[] ea, char[] digit) {
		// pass through all our EasyCharacter
		for(int i = 0; i < ea.length; i++) {
			// get the bit to swap
			int index = i % keySwap1.length;
			int from = keySwap1[index];
			int to = keySwap2[index];
			// get the bit representation of our EasyCharacter
			char[] zeroAndOne = ea[i].getBinaryChar();
			// save first char
			char tmp = zeroAndOne[from];
			// put second at its place
			zeroAndOne[from] = zeroAndOne[to];
			// put back the saved one
			zeroAndOne[to] = tmp;
			// use the Integer parseInt() method with the base specified to read the binary digit
			int newVal = Integer.parseInt(new String(zeroAndOne), 2);
			// and store it in the encoded/decoded digit
			digit[i] = (char) newVal;
		}
	}
	
	/**
	 * Perform the rotate for encoding or decoding
	 * Encoding will rotate right decoding left
	 * the sign of number to shift received as parameter will tell me wich side to rotate
	 */
	private void performRotate(CharAndBits[] ea, char[] digit, int nbRotate) {
		// build an array of char containing the bit representation of the whole message
		// we are using a StringBuilder to perform this operation
		StringBuilder sb = new StringBuilder(ea.length);
		for(CharAndBits c : ea)
			sb.append(c.toBinaryString());
		// make the char[] array 
		char[] orig = sb.toString().toCharArray();

		// create the destination array
		char[] dest = new char[orig.length];
		
		// encoding
		if(nbRotate > 0) {
			// were we will start to copy
			int where = nbRotate;
			// pass through all the bits represented by a char '0' or '1'
			for(char c : orig) {
				// wrap around
				if(where >= dest.length)
					where = 0;
				// put the char at that position
				dest[where++] = c;
			}
		}
		else {	// decoding
			int from = -nbRotate;   // + because nbRotate is negative
			for(int i = 0; i < dest.length; i++) {
				// wrap around
				if(from >= dest.length)
					from = 0;
				// put the char at that position
				dest[i] = orig[from++];			
			}
		}
		
		// now read back 8 bits by 8 bits the new bits rotated
		int k = 0;  // index in digit[] array
		for(int i = 0; i < dest.length; i+= 8) {
			String heightBits = new String(dest, i, 8);		// build String of 8 char '0' or '1'
			int value = Integer.parseInt(heightBits, 2);
			digit[k++] = (char) value;
		}
	}
	/**
	 * To test the class
	 */
	public static void main(String[] args) {

		// -------------------- generic test ----------------------------------
		// ----------- these lines can be removed in production ---------------
		
		// the 2 keys
		String swapKey = "021324";    
		String rotateKey = "-1,10,23,-14";     // will garanty a rotate of 2
		// create our SwapAndRotate object
		SwapAndRotate sar = new SwapAndRotate(swapKey, rotateKey);
		String msg = "DreamInCode";
		char[] digit, digitBack;
		String decoded;
		
		// SWAP test
		// print the message and its binary representation
		System.out.println("SWAP test Message: " + msg);
		System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
		
		// encode with Swap
		digit = sar.encode(msg, SwapAndRotate.SWAP);
		// print the swap version
		System.out.println("Swap:");
		System.out.println(CharAndBits.toBinaryString(digit));
		
		// decode message back
		digitBack = sar.decode(new String(digit), SwapAndRotate.SWAP);
		// print back the decoded version in binary
		System.out.println("Swap Back:");
		System.out.println(CharAndBits.toBinaryString(digitBack));
		
		// message back
		decoded = new String(digitBack);
		System.out.println("Decoded: \"" + decoded + "\" is it the same as \"" + msg + "\" ? : " + decoded.equals(msg));
		System.out.println();
		
		// ROTATE test
		// print the message and its binary representation
		System.out.println("ROTATE test Message: " + msg);
		System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
		
		// encode with Rotate
		digit = sar.encode(msg, SwapAndRotate.ROTATE);
		// print the Rotate version
		System.out.println("Rotated:");
		System.out.println(CharAndBits.toBinaryString(digit));
		
		// decode message back
		digitBack = sar.decode(new String(digit), SwapAndRotate.ROTATE);
		// print back the decoded version in binary
		System.out.println("Rotated Back:");
		System.out.println(CharAndBits.toBinaryString(digitBack));
		
		// message back
		decoded = new String(digitBack);
		System.out.println("Decoded: \"" + decoded + "\" is it the same as \"" + msg + "\" ? : " + decoded.equals(msg));
		System.out.println();
		
		// BOTH test
		// print the message and its binary representation
		System.out.println("BOTH test Message: " + msg);
		System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
		
		// encode with Rotate
		digit = sar.encode(msg, SwapAndRotate.BOTH);
		// print the Rotate version
		System.out.println("Swapped & Rotated:");
		System.out.println(CharAndBits.toBinaryString(digit));
		
		// decode message back
		digitBack = sar.decode(new String(digit), SwapAndRotate.BOTH);
		// print back the decoded version in binary
		System.out.println("Swap & Rotated Back:");
		System.out.println(CharAndBits.toBinaryString(digitBack));
		
		// message back
		decoded = new String(digitBack);
		System.out.println("Decoded: \"" + decoded + "\" is it the same as \"" + msg + "\" ? : " + decoded.equals(msg));
		System.out.println();
		
		
		// --------------- end of generic test --------------------------------
		
		// user input
		Scanner scan = new Scanner(System.in);
		// prompt for the key
		String keySwap;
		System.out.println("Enter SWAP key composed of pairs of bit number from 0 to 7");
		do {
			System.out.print("Enter key like 123464: ");
			keySwap = scan.nextLine();
		} while (!SwapAndRotate.bValidateSwapKey(keySwap));
		
		// prompt for the Rotate key
		String keyRotate;
		System.out.println("Enter ROTATE key composed of integer separated by ,");
		do {
			System.out.print("Enter key like 12,546,323: ");		
			keyRotate = scan.nextLine();
		} while(!SwapAndRotate.bValidateRotateKey(keyRotate));
		
		// create SwapAndRotate object
		SwapAndRotate obj = new SwapAndRotate(keySwap, keyRotate);
		// get message
		System.out.print("Enter message to encode/decode:");
		String message = scan.nextLine();

		// test SWAP
		// print message with bits
		System.out.println("SWAP Original message is: " + message);
		System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
		
		char[] swapDigit = obj.encode(message, SwapAndRotate.SWAP);
		System.out.println("After SWAP:");
		System.out.println(CharAndBits.toBinaryString(swapDigit));
		char[] swapBack = obj.decode(new String(swapDigit), SwapAndRotate.SWAP);
		System.out.println("After decode:");
		System.out.println(CharAndBits.toBinaryString(swapBack));
		System.out.println("Decoded message: " + new String(swapBack));
		System.out.println();
		
		// test ROTATE
		// print message with bits
		System.out.println("ROTATE Original message is: " + message);
		System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
		
		char[] rotateDigit = obj.encode(message, SwapAndRotate.ROTATE);
		System.out.println("After ROTATE:");
		System.out.println(CharAndBits.toBinaryString(rotateDigit));
		char[] rotateBack = obj.decode(new String(rotateDigit), SwapAndRotate.ROTATE);
		System.out.println("After decode:");
		System.out.println(CharAndBits.toBinaryString(rotateBack));
		System.out.println("Decoded message: " + new String(rotateBack));
		System.out.println();
		
		// test BOTH
		// print message with bits
		System.out.println("BOTH Original message is: " + message);
		System.out.println(CharAndBits.toBinaryString(msg.toCharArray()));
		
		char[] bothDigit = obj.encode(message, SwapAndRotate.BOTH);
		System.out.println("After BOTH:");
		System.out.println(CharAndBits.toBinaryString(bothDigit));
		char[] bothBack = obj.decode(new String(bothDigit), SwapAndRotate.BOTH);
		System.out.println("After decode:");
		System.out.println(CharAndBits.toBinaryString(bothBack));
		System.out.println("Decoded message: " + new String(bothBack));
		System.out.println();
		
		
	}
	
}




And now the GUI application. Notice, when the ROTATE mechanism is used, how all the bit fields are updated when you dynamically change the key or the message to encode.

This GUI is also a nice tutorial on how to use JTable

SwapAndRotateGui.java


import java.awt.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;

/**
 * A GUI that use the SwapAndRotate class to encode/decode messages
 * In this GUI we cannot prompt for a String to decode as the swapped and rotated bits
 * in byte may generate characters that may (most probably) not be displayable
 * 
 * The GUI display the binary form of each letter in the message
 * their coded representaion for SWAP, ROTATE and BOTH
 * The bottom of the frame shows if each key SWAP and ROTATE are valid
 */
public class SwapAndRotateGui extends JFrame {

	private static final long serialVersionUID = 1L;

	// the SwapAndRotate class to encode/decode
	private SwapAndRotate war;

	// the keys to use
	private JTextField keySwapText, keyRotateText;
	// The message to encode
	private JTextField clearTextIn;
	
	// The message saying if the keys are valid
	private boolean keySwapValid, keyRotateValid;
	private JLabel swapValidLabel, rotateValidLabel;
	
	// the original message
	private char[] msgChar = new char[0];
	// the decoded messages
	private char[] swapEncoded, rotateEncoded, bothEncoded;
	// the encoded messages
	private char[] swapDecoded, rotateDecoded, bothDecoded;
	
	// The JTable shown in the CENTER region
	private JTable table;
	private MyModel myModel;
	// Its panel
	private JPanel centerPanel;
	// it's headers
	private static final String[] header = {"Orig msg", "Swappped crypted", "Rotated crypted", "Both crypted",
											 "Swap decripted", "Rotate decrypted", "Both decrypted"};
	// mnemonic for more descriptive values in the AbstractModel
	private static final int ORIG = 0, SWAPPED = 1, ROTATED = 2, BOTH = 3,
	                                   SWAP_BACK = 4, ROTATE_BACK = 5, BOTH_BACK = 6;
	
	/**
	 * Constructor
	 */
	SwapAndRotateGui() {
		super("SwapAndRotate encoding/decoding");
		// we will use a BorderLayout to store our GUI component
		setLayout(new BorderLayout());

		// the SwapAndRotate object init the keys to ""
		war = new SwapAndRotate("", "");

		// the Listener when one of the key changes
		DocumentListener dc = new KeyListener();
		// The NORTH region will contain a JPanel where the keys and the message can be entererd
		JPanel north = new JPanel(new GridLayout(7,1, 2, 2));
		// the SWAP key
		north.add(createCenteredLabel("The SWAP key composed of pairs of bits to swap like 071625"));
		keySwapText = new JTextField(50);
		keySwapText.getDocument().addDocumentListener(dc);
		north.add(keySwapText);
		// the ROTATE key
		north.add(createCenteredLabel("The ROTATE key composed of numbers of bits to rotate like 12,-34,92,18"));
		keyRotateText = new JTextField(50);
		keyRotateText.getDocument().addDocumentListener(dc);
		north.add(keyRotateText);
		// the message
		north.add(createCenteredLabel("Enter the message to decode here"));
		clearTextIn = new JTextField(50);
		clearTextIn.getDocument().addDocumentListener(new ClearListener());
		north.add(clearTextIn);
		// a gap
		north.add(new JLabel(" "));
		// add this panel to the top of the screen
		add(north, BorderLayout.NORTH);
		
		// in the CENTER region of the frame we will put a JTable containing all the 
		// encrypted/decripted bits
		myModel = new MyModel();
		table = new JTable(myModel);
		centerPanel = new JPanel(new GridLayout(1,1));
		centerPanel.add(new JScrollPane(table));
        add(centerPanel, BorderLayout.CENTER);


		// The SOUTH region contains 2 JLabel specifying if the keys are valid
		JPanel south = new JPanel(new GridLayout(1, 2));
		// create the 2 labels using our centered label factory
		swapValidLabel = createCenteredLabel("");
		rotateValidLabel = createCenteredLabel("");
		// we make them opaque as their foreground color changes depending if
		// the key is valid or not
		swapValidLabel.setOpaque(true);
		rotateValidLabel.setOpaque(true);
		// put them in the panel
		south.add(swapValidLabel);
		south.add(rotateValidLabel);
		updateValidLabel();
		add(south, BorderLayout.SOUTH);
		
		// standard operation to show the JFrame
		this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setBounds(30, 30, 900, 500);
		setVisible(true);
	}

	/**
	 * A method to create a JLabel with foreground color Blue and with text centered
	 */
	private JLabel createCenteredLabel(String text) {
		JLabel label = new JLabel(text);
		label.setHorizontalAlignment(SwingConstants.CENTER);
		label.setForeground(Color.BLUE);
		return label;
	}
	
	/**
	 * One of the 2 key has changed 
	 */
	private void updateKeyString() {
		String keyStr;

		// update both keys from their JTextField
		keyStr = keySwapText.getText();
		// check if SWAP key is valid
		keySwapValid = SwapAndRotate.bValidateSwapKey(keyStr);
		war.setKeySwap(keyStr);

		// check if ROTATE key is valid
		keyStr = keyRotateText.getText();
		keyRotateValid = SwapAndRotate.bValidateRotateKey(keyStr);
		war.setKeyRotate(keyStr);
		
		// update the JLabel that says if the key are valid
		updateValidLabel();
		// set the color of the JTextTextFields displaying the key in black if the key is valid
		// in RED is the key is not valid
		if(keySwapValid)
			keySwapText.setForeground(Color.BLACK);
		else
			keySwapText.setForeground(Color.RED);
		if(keyRotateValid)
			keyRotateText.setForeground(Color.BLACK);
		else
			keyRotateText.setForeground(Color.RED);
			
		// update the keys in the SwapAndRotateObject
		// and update the string to encode
		updateStringToEncode();
	}
	
	/**
	 * To update the string to be coded
	 */
	private void updateStringToEncode() {
		// get the text from the JTextField
		String line = clearTextIn.getText();
		// make the char[] array out of it to be displayed in the JTable
		msgChar = line.toCharArray();
		// build the encrypted char[] for the 3 coding algorithms
		swapEncoded = war.encode(line, SwapAndRotate.SWAP);
		rotateEncoded = war.encode(line, SwapAndRotate.ROTATE);
		bothEncoded = war.encode(line, SwapAndRotate.BOTH);
		// build the decrypted char[] for the 3 coding algorithms
		swapDecoded = war.decode(new String(swapEncoded), SwapAndRotate.SWAP);
		rotateDecoded = war.decode(new String(rotateEncoded), SwapAndRotate.ROTATE);
		bothDecoded = war.decode(new String(bothEncoded), SwapAndRotate.BOTH);
		// inform the model that the table contain changed 
		myModel.fireTableDataChanged();
	}
	
		
	/** 
	 * To update the labels saying if the keys are valid
	 */
	private static final Color darkGreen = new Color(0, 120, 0);
	private void updateValidLabel() {
		// updates the text of both labels saying if the key is valid
		// update also the foregound and background colors
		// valid: WHITE and GREEN
		// no valid: YELLOW and RED
		if(keySwapValid) {
			swapValidLabel.setText("SWAP key is valid");
			swapValidLabel.setForeground(darkGreen);
			swapValidLabel.setBackground(Color.WHITE);
		}
		else {
			swapValidLabel.setText("SWAP key is NOT valid");
			swapValidLabel.setForeground(Color.RED);			
			swapValidLabel.setBackground(Color.YELLOW);
		}
		
		if(keyRotateValid) {
			rotateValidLabel.setText("ROTATE key is valid");
			rotateValidLabel.setForeground(darkGreen);
			rotateValidLabel.setBackground(Color.WHITE);
		}
		else {
			rotateValidLabel.setText("ROTATE key is NOT valid");
			rotateValidLabel.setForeground(Color.RED);			
			rotateValidLabel.setBackground(Color.YELLOW);
		}
		
		
	}
	/**
	 * To start the GUI
	 */
	public static void main(String[] args) {
		new SwapAndRotateGui();
	}

	/**
	 * A listener to be informed whenever the JTextField of the clear text is changed
	 */
	private class ClearListener implements DocumentListener {
		@Override
		public void changedUpdate(DocumentEvent arg0) {
			updateStringToEncode();
		}
		@Override
		public void insertUpdate(DocumentEvent arg0) {
			updateStringToEncode();
		}
		@Override
		public void removeUpdate(DocumentEvent arg0) {
			updateStringToEncode();
		}
	}
	
	/**
	 * A listener to be informed whenever the JTextField of the SWAP or ROTATE key is changed
	 */
	private class KeyListener implements DocumentListener {
		@Override
		public void changedUpdate(DocumentEvent arg0) {
			updateKeyString();
		}
		@Override
		public void insertUpdate(DocumentEvent arg0) {
			updateKeyString();
		}
		@Override
		public void removeUpdate(DocumentEvent arg0) {
			updateKeyString();
		}
	}

	/** 
	 * A class that extends AbstractTableModel to povide the binary representation
	 * of every cell of the JTable in the center panel
	 */
	private class MyModel extends AbstractTableModel {

		private static final long serialVersionUID = 1L;

		// the number of columns is the length of the header of these columns
		public int getColumnCount() {
			return header.length;
		}

		// name of each colum
		public String getColumnName(int column) {
			return header[column];
		}
		// return the row count
		public int getRowCount() {
			// return the length of the message in the JTextField
			return clearTextIn.getText().length();
		}

		// the JTable want's to know what to print there
		public Object getValueAt(int row, int col) {
			// validate first the row
			if(row >= msgChar.length)
				return "";
			// the CharAndBits to display
			CharAndBits cab = null;
			// the cell text
			String str;
			
			// depending of the column display the correct text field
			switch(col) {
			    // the only one where the char preceedes the binary String
				case ORIG:
					cab = new CharAndBits(msgChar[row]);
					str = cab.getPrintableChar() + " - " + cab.toBinaryString();
					return str;
				case SWAPPED:
					if(row >= swapEncoded.length)
						break;
					cab = new CharAndBits(swapEncoded[row]);
					break;					
				case ROTATED:
					if(row >= rotateEncoded.length)
						break;
					cab = new CharAndBits(rotateEncoded[row]);
					break;					
				case BOTH:
					if(row >= bothEncoded.length)
						break;
					cab = new CharAndBits(bothEncoded[row]);
					break;					
				case SWAP_BACK:
					if(row >= swapDecoded.length)
						break;
					cab = new CharAndBits(swapDecoded[row]);
					break;					
				case ROTATE_BACK:
					if(row >= rotateDecoded.length)
						break;
					cab = new CharAndBits(rotateDecoded[row]);
					break;					
				case BOTH_BACK:
					if(row >= bothDecoded.length)
						break;
					cab = new CharAndBits(bothDecoded[row]);
					break;					
			}
            // common formatting the binary string followed by the printable version of the char
			// unless not defined yet due to the way the GUI refresh the JTable
			if(cab == null)
				return "";
			str = cab.toBinaryString() + " - " + cab.getPrintableChar();
			return str;
		}
		
	}

}



Is This A Good Question/Topic? 1
  • +

Page 1 of 1