Rot13 (Java)

From LiteratePrograms

Jump to: navigation, search
Other implementations: Forth | Haskell | Java | Scheme | Sed

Here, we present a Java program that performs Rot13 encoding and/or decoding for text passed on the command line or via a filter.

The heart of this cipher is a simple character substitution that moves the letters greater than or equal to 'n' 13 places towards the front of the alphabet and letters less than or equal to 'm' 13 places towards the back. This is done separately for the lower case and upper case letters. Punctuation and non-English characters are left unchanged.

For example, if the plain text was:

Now is the Winter of our Discontent,
Made glorious Summer by this Son of Yorke:

The encoded string would be:

Abj vf gur Jvagre bs bhe Qvfpbagrag,
Znqr tybevbhf Fhzzre ol guvf Fba bs Lbexr:

If the Rot13 algorithm is reapplied to the encoded string, the original plain text is returned.

We also take the opportunity to introduce the reader to the JUnit test framework and the Apache Ant build tool, both of which are a standard part of the professional Java development toolbox.

Contents

The Algorithm

In the following, c is the character currently being encoded.

<<encode c>>=
if      (c >= 'a' && c <= 'm') c += 13;
else if (c >= 'n' && c <= 'z') c -= 13;
else if (c >= 'A' && c <= 'M') c += 13;
else if (c >= 'N' && c <= 'Z') c -= 13;

A function to use this method would simply take a string, extract the characters, encode them using the character encoding scheme, and rebuild a string from the encoded text, as follows:

<<encoder>>=
/**
 * Encode plain text using the Rot13 algorithm.
 * @param plainText the plain text message.
 * @returns the plain text message encoded using the Rot13 algorithm.
 */
public static String encode(String plainText) {
  // deal with the case that method is called with null argument
  if (plainText == null) return plainText;
  
  // encode plainText
  String encodedMessage = "";
  for (int i = 0; i < plainText.length(); i++) {
    char c = plainText.charAt(i);
    encode c
    encodedMessage += c;
  }
  return encodedMessage;
}

Testing the Algorithm

For the test we shall use part of the opening soliloquy quote from Act I Scene I of William Shakespeare's Richard III.

<<declare test strings>>=
private String plainText;
<<define test strings>>=
plainText = "Now is the Winter of our Discontent,\n" +
            "Made glorious Summer by this Son of Yorke:\n" +
            "And all the clouds that lowr'd vpon our house\n" +
            "In the deepe bosome of the Ocean buried.\n" +
            "Now are our browes bound with Victorious Wreathes,\n" +
            "Our bruised armes hung vp for Monuments;\n" +
            "Our sterne Alarums chang'd to merry Meetings;\n" +
            "Our dreadfull Marches, to delightfull Measures.\n" +
            "Grim-visag'd Warre, hath smooth'd his wrinkled Front:\n" +
            "And now, in stead of mounting Barbed Steeds,\n" +
            "To fright the Soules of fearfull Aduersaries,\n" +
            "He capers nimbly in a Ladies Chamber,\n" +
            "To the lasciuious pleasing of a Lute.";

To test the algorithm, it is sufficient to verify that the cipher text produced by Rot13.encode is an Rot13 translation of the plain text. This was done by feeding the plain text into an online Rot13 translator (http://www.rot13.com/index.php was used) and assigning the result to the string cipherText.

<<declare test strings>>=
private String cipherText;
<<define test strings>>=
cipherText = "Abj vf gur Jvagre bs bhe Qvfpbagrag,\n" + 
             "Znqr tybevbhf Fhzzre ol guvf Fba bs Lbexr:\n" + 
             "Naq nyy gur pybhqf gung ybje'q icba bhe ubhfr\n" + 
             "Va gur qrrcr obfbzr bs gur Bprna ohevrq.\n" + 
             "Abj ner bhe oebjrf obhaq jvgu Ivpgbevbhf Jerngurf,\n" + 
             "Bhe oehvfrq nezrf uhat ic sbe Zbahzragf;\n" + 
             "Bhe fgrear Nynehzf punat'q gb zreel Zrrgvatf;\n" + 
             "Bhe qernqshyy Znepurf, gb qryvtugshyy Zrnfherf.\n" + 
             "Tevz-ivfnt'q Jneer, ungu fzbbgu'q uvf jevaxyrq Sebag:\n" + 
             "Naq abj, va fgrnq bs zbhagvat Oneorq Fgrrqf,\n" + 
             "Gb sevtug gur Fbhyrf bs srneshyy Nqhrefnevrf,\n" + 
             "Ur pncref avzoyl va n Ynqvrf Punzore,\n" + 
             "Gb gur ynfpvhvbhf cyrnfvat bs n Yhgr.";

For the actual test, we shall use the JUnit test framework, the de facto standard for automated unit testing in Java. For the first test, we verify that the cipher text produced by Rot13.encode() matches that which we produced independently:

<<test encoder>>=
public void testEncode() {
  assertEquals(cipherText, Rot13.encode(plainText));
}

The test is self explanatory. When run, the JUnit framework will automatically execute the testEncode method (and all other methods starting with the word test). The test assertion assertEquals fails by throwing an exception if cipherText does not match the string produced by the method under test Rot13.encode(plainText). Such failures are marked as test failures by the JUnit framework. If the test is successful, JUnit silently moves on to the next test. Thus in JUnit testing silence is golden!

The Rot13 algorithm is symmetric, thus if we encode the encoded text, we retrieve the original message. Our second test will verify that this happens.

<<test decoder>>=
public void testDecode() {
    assertEquals(plainText, Rot13.encode(cipherText));
}

To ensure that the test framework is properly set up, we put the code to define the test strings into JUnit's setUp method. This method will be called before each test is executed and ensures that the test data is consistent.

<<set up test framework>>=
public void setUp() {
    define test strings
}

The test program itself is simply a standard Java class which imports the JUnit framework. The test method inherits all of its test behaviour from JUnit's TestCase class.

<<Rot13Test.java>>=
package org.literateprograms.ciphers;

import junit.framework.TestCase;

public class Rot13Test extends TestCase {

    declare test strings
	
    set up test framework

    test encoder

    test decoder

}

Building the Files: A Standardized Java Project

In order to prepare for the discussion of Ant to follow, it is useful at this point to establish a proper build environment for our project source files and the other artifacts to come. So, to begin as we mean to go on, you should now create a new directory for your project, say rot13 and in this directory you should create a further directory called src. Now download the code and place the *.java source files into the rot13/src directory.

To compile the program and run the tests you will need to include the JUnit framework in your classpath. The easiest way to do this is to put a copy of junit.jar (which comes with the JUnit distribution) into a new directory called rot13/lib directory.

After this step, the structure of your project files will be:

rot13
  |
  \- src
  |   |
  |   \- Rot13.java
  |   |
  |   \- Rot13Test.java
  |
  \- lib
      |
      \- junit.jar


Now create a directory rot13/build/classes for the generated class files:

 $ cd rot13; mkdir build; mkdir build/classes

and, from the project directory, compile the Rot13 and Rot13Test class files:

 $ javac -cp 'build/classes;lib/junit.jar' -d build/classes src/*.java

Finally run the tests using the JUnit test runner (command for Junit 3.8.1 described):

 $ java -cp 'build/classes;lib/junit.jar' junit.textui.TestRunner org.literateprograms.ciphers.Rot13Test
 ..
 Time: 0.016
 
 OK (2 tests)

As you can see, JUnit runs the tests and if it reports a simple OK message, all is well.

User's guide to the program

We would like the program to behave like a standard filter program (albeit with Java conventions). Assuming that the class file for Rot13 is packaged up in an "executable" JAR file called rot13.jar, a standard way to use the program would be:

 java -jar rot13.jar < plaintext.txt

In which the text contained in the file plaintext.txt would be passed to the program through standard input, translated using the Rot13 encoding and the result would appear on the console (standard output). This form of program can also act as a filter as in:

 cat plaintext.txt | java -jar rot13.jar 

and can make use of file redirection as in:

 java -jar rot13.jar < plaintext.txt > cipher.txt

As an extra feature, we would like to use a command line argument to encode a string provided at the command line. For this we would like the program to give:

 $ java -jar rot13.jar -e "Now is the Winter of our Discontent"
 Abj vf gur Jvagre bs bhe Qvfpbagrag

If we call the program with unexpected arguments, or using the -h argument, we'd like it to produce a simple usage message:

 $ java -jar rot13.jar -h 
 Usage: java -jar rot13.jar [-h] [-e "text to be encoded"] [< plaintext] [> ciphertext]
 where arguments are:
 -e encode text on the command line
 -h print this message

The Command-Line Interface (CLI) Program

This type of program we wish to produce is typical of the programs that are featured in classic command line interfaces such as those provided in Unix and MS-DOS. The development of a Java version is a useful introduction to simple I/O and the arguments array in the Java main method. All of the action is contained in the main method:

<<main method>>=
public static void main(String [] args)
throws IOException {

   String input = ""; // data to be encoded

   check and process arguments

   open input stream if required

   encode data
 
   exit

}

Note we declare that main throws IOException because we do not want to deal with I/O errors.

Processing the Command Line Arguments

To set up the program for action, we need to interpret and process the command-line arguments. These will be passed as Strings (one string per word) in the String array args. We expect either one, two or three arguments. Anything else is an error and we print an error message, the usage message and exit the program. By convention, the error and usage message are printed on standard error (Java System.err) and the program exit is actioned by calling System.exit(1), which by convention this means abnormal termination.

<<check and process arguments>>=
if (args.length <= 3) {
  process arguments
}
else {
  System.err.println("Error: Too many arguments.\n" + usage());
  System.exit(1);
}

The usage message is simply the message described in the user guide returned from usage() as a string:

<<define usage method>>=
private static String usage() {
  return "Usage: java -jar rot13.jar [-h] [-e \"text to be encoded\"] [< plaintext] [> ciphertext]\n" +
  "where arguments are:\n" +
  "-e encode text on the command line\n" +
  "-h print this message";
} 


The arguments are processed as follows:

  1. If the argument is -e the following argument is taken to be the text to be encoded or decoded. Because the arguments are passed as one word per argument element, a long text will have to be passed to the program in quotes as in java -jar rot13.jar -e "A Very Short Message".
  2. If the argument is -h the usage message is printed and the program exits by executing System.exit(0), which by convention means OK.
  3. Any other arguments are rejected and the program exits with abnormal exit status.

Here is the implementation of this algorithm:

<<process arguments>>=
int argNumber = 0;
while (argNumber < args.length) {
  if ("-e".equals(args[argNumber])) {
    argNumber ++;
    input = args[argNumber];
    argNumber ++;
  }
  else if ("-h".equals(args[argNumber])) {
     System.err.println(usage());
     System.exit(0);
  }
  else {
     System.err.println("Error: unexpected argument " + args[argNumber]);
     System.err.println(usage());
     System.exit(1);
  }
}

Please note that if the -e argument has been accepted, the text to be encoded will be in the String input. If not, this string will be the empty string.

Read Data from the Standard Input Stream

If the string to be encoded was not passed to the program using the -e argument, the String input will still be empty. In this case, e assume that we wnat to take the plain text from standard input. The test for this is:

<<open input stream if required>>=
if (input.length() == 0) {
  open standard input and extract data into input string
}

Setting up a data stream that can textual read from standard input is rather more complex in Java than in most other comparable languages. The complexity is accounted for by the supposition that the data input could come from a range of sources, including files, URLs, various compressed archives, JAR files and encrypted data channels, etc. Still, it remains something of a mystery why the most common case, read from a keyboard (a channel known as standard input), could not have been made as simple as the case of writing to the console (standard output). The latter is achieved by a simple call to System.out.println(String). The former requires rather more set up: we have to wrap the DataStream System.in in an InputStreamReader and then wrap the result in a BufferedReader.

<<open standard input and extract data into input string>>=
BufferedReader in = new BufferedReader(
   new InputStreamReader(System.in));

Once we have created the buffered reader, we can read the input using the readLine. This will return null when the end-of-file (EOF) is reached, thus we can read the lines in the body of a while statement, and concatenate each line with the input string.

<<open standard input and extract data into input string>>=
String s;
while ((s = in.readLine()) != null) {
  input += s + "\n";
}

Note that the realLine() does not return the end-of-line character, so we add it back when we construct the string.

Encode the Data and Write the Results to the Standard Output Stream

<<encode data>>=
System.out.println(encode(input));

Exit the Program

To exit the program, we simply call System.exit(0) which signals success.

<<exit>>=
System.exit(0);


The Final Program

Here is the final program.

<<Rot13.java>>=
package org.literateprograms.ciphers;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Rot13 {

   encoder

   main method

   define usage method
}

Assuming the project structure described earlier, we compile the program in this way:

$ javac -cp 'build/classes' -d build/classes src/Rot13.java

For convenience in later deployment of the program, we package the compiled classes into up into an executable JAR file. On Unix-like operating systems we do this like this:

$ echo Main-Class: org.literateprograms.ciphers.Rot13>myManifest
mkdir build/jar
jar cfm build/jar/rot13.jar myManifest -C build/classes .

Note: there really should be no space between org.literateprograms.ciphers.Rot13, >, and myManifest.

We can now run the program like this:

$ java -jar build/jar/rot13 -h
Usage: java -jar rot13.jar [-h] [-e "text to be encoded"] [< plaintext] [> ciphertext]
where arguments are:
-e encode text on the command line
-h print this message

$ java -jar build/jar/rot13 -e "The general is going to advance. Send reinforcements"

Gur trareny vf tbvat gb nqinapr. Fraq ervasbeprzragf

For convenience in testing here is our test data as a text file:

<<plaintext.txt>>=
Now is the Winter of our Discontent,
Made glorious Summer by this Son of Yorke:
And all the clouds that lowr'd vpon our house
In the deepe bosome of the Ocean buried.
Now are our browes bound with Victorious Wreathes,
Our bruised armes hung vp for Monuments;
Our sterne Alarums chang'd to merry Meetings;
Our dreadfull Marches, to delightfull Measures.
Grim-visag'd Warre, hath smooth'd his wrinkled Front:
And now, in stead of mounting Barbed Steeds,
To fright the Soules of fearfull Aduersaries,
He capers nimbly in a Ladies Chamber,
To the lasciuious pleasing of a Lute.

Save this file into the rot13 directory and use the program in its filter form to produce the output that we expect:

 $ java -jar build/jar/rot13.jar < plaintext.txt
 Abj vf gur Jvagre bs bhe Qvfpbagrag,
 Znqr tybevbhf Fhzzre ol guvf Fba bs Lbexr:
 Naq nyy gur pybhqf gung ybje'q icba bhe ubhfr
 Va gur qrrcr obfbzr bs gur Bprna ohevrq.
 Abj ner bhe oebjrf obhaq jvgu Ivpgbevbhf Jerngurf,
 Bhe oehvfrq nezrf uhat ic sbe Zbahzragf;
 Bhe fgrear Nynehzf punat'q gb zreel Zrrgvatf;
 Bhe qernqshyy Znepurf, gb qryvtugshyy Zrnfherf.
 Tevz-ivfnt'q Jneer, ungu fzbbgu'q uvf jevaxyrq Sebag:
 Naq abj, va fgrnq bs zbhagvat Oneorq Fgrrqf,
 Gb sevtug gur Fbhyrf bs srneshyy Nqhrefnevrf,
 Ur pncref avzoyl va n Ynqvrf Punzore,
 Gb gur ynfpvhvbhf cyrnfvat bs n Yhgr.

And the final proof is:

 $ java -jar build/jar/rot13.jar < plaintext.txt | java -jar build/jar/rot13.jar
 Now is the Winter of our Discontent,
 Made glorious Summer by this Son of Yorke:
 And all the clouds that lowr'd vpon our house
 In the deepe bosome of the Ocean buried.
 Now are our browes bound with Victorious Wreathes,
 Our bruised armes hung vp for Monuments;
 Our sterne Alarums chang'd to merry Meetings;
 Our dreadfull Marches, to delightfull Measures.
 Grim-visag'd Warre, hath smooth'd his wrinkled Front:
 And now, in stead of mounting Barbed Steeds,
 To fright the Soules of fearfull Aduersaries,
 He capers nimbly in a Ladies Chamber,
 To the lasciuious pleasing of a Lute.

If we want to use the program in production, we simply need to put rot13.jar somewhere where java will be able to find it. Alternatively, add rot13 to the Java classpath and call it like this:

 $ java -cp full_path_to/rot13.jar org.literateprograms.ciphers.Rot13 -h

Building and Testing the Program with Ant

Along with the JUnit framework, used and illustrated in this article for unit testing, another standard tool in the professional Java developer's toolbox is the Apache Ant build tool. To conclude this article, we shall demonstrate how Ant can be used to control the whole build process from compiling the Java source and test code, to running the JUnit tests all the way to packaging the program up as a jar file for deployment. We shall create and document a typical build file for this simple Java project which can also serve as a template for further projects. We shall conclude the article by demonstrating its use in building and running the Rot13 program. We will not document the installation of Ant nor the set up required to incorporate JUnit into an Ant build system. We assume that the interested reader will consult the documentation for details of how to do this.

The build file developed here is similar to the one described in Hello World with Ant, but obviously presented here in a literate form. It builds upon the proposed project structure described earlier with the additional constraint that the source files should be stored according to the structure implied by the package structure of the class files. Thus we need to create a directory structure org/literateprograms/ciphers to match the package statement package org.literateprograms.ciphers; used in both Rot13.java and Rot13Test.java. This in any case matches what most IDEs will do with the source files. Once you have created the directory structure, move Rot13.java and Rot13Test.java from src into src/org/literateprograms/ciphers.

In order to build our project we need to think about the steps that we will go through. Essentially these are

  1. compile our program source code
  2. package the program into rot13.jar
  3. test the program using JUnit
  4. run the program using java -jar rot13.jar

Each step becomes a target in the so-called build file. This build file is written in a well-formed XML file, which is by convention usually called build.xml. Thus, in outline, our build file will be:

<<build.xml>>=
<project name="rot13" basedir="." default="main">

  properties

  clean project

  compile source code

  package jar file

  compile and run unit tests

  run program

  pseudo targets

</project>

To compile the Java source file we use a following compile target which creates the required build directory ${classes.dir} (unless it already exists) and compiles all the Java source files found in ${src.dir} storing the resulting class files in the build directory.

<<compile source code>>=
<target name="compile">
  <mkdir dir="${classes.dir}"/>
  <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
</target>

This produces exactly the same result as the Java compilation step shown earlier, with the exception that the compilation is only performed for files for which the source code is newer than the class file.

The next target is package jar file. This depends on the code being compiled first, so the target jar depends on the target compile:

<<package jar file>>=
<target name="jar" depends="compile">
  <mkdir dir="${jar.dir}" />
  <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
    <manifest>
       <attribute name="Main-Class" value="${main-class}" />
    </manifest>
  </jar>
</target>

Note also that the manifest needed to create a runnable jar is also created for us.

To run the program we create another target that depends on the target jar:

<<run program>>=
<target name="run" depends="jar">
  <java fork="true" classname="${main-class}">
     <classpath>
        <path id="application" location="${jar.dir}/${ant.project.name}.jar"/>
     </classpath>
     <arg line="-h" />
  </java>
</target>

With the property values ${jar.dir} = build/jar and ${ant.project.name} = rot13, and ${main-class} = org.literateprograms.ciphers.Rot13 substituted, this is exactly equivalent to the command:

$ java -cp build/jar/rot13.jar org.literateprograms.ciphers.Rot13 -h

except that it is only executed if compilation and packaging is successful.

The JUnit unit tests can also be automatically run for us. Modern versions of Ant include the necessary junit.jar and the necessary support classes. Thus we do not even need to have lib/junit.jar in our project and it can be safely removed. The unit testing target, at is simplest, depends on compilation and looks like this:

<<compile and run unit tests>>=
<target name="test" depends="jar">
  <junit printsummary="yes">
     <classpath>
       <path refid="application"/>
     </classpath>
            
     <batchtest fork="yes">
        <fileset dir="${src.dir}" includes="**/*Test.java"/>
     </batchtest>
  </junit>
</target>

This target runs all the unit tests that it finds in the ${src.dir} directory (which by convention will end in *Test.java), reusing the classpath used to run the application in the run target.

The clean target cleans the project removing all generated *.class and *.jar files. Because of the structure of our project directory, this is performed simply by deleting the whole ${build.dir} directory.

<<clean project>>=
<target name="clean">
  <delete dir="${build.dir}"/>
</target>

To make the build file more generally useful, we have used properties. These are similar in function to environment variables in a Makefile or #define statements in C source. They declare name=value pairs that parameterize the targets. Thus to re-use the build file in another project it would be sufficient to copy it into the new project directory, rename the name="rot13" attribute in the project tag and adjust the <coce>main-class</code> property. Providing that you reuse the project structure described above, the build-file should work without any further change. Here are the properties in full:

<<properties>>=
<property name="src.dir"     value="src"/>

<property name="build.dir"   value="build"/>
<property name="classes.dir" value="${build.dir}/classes" />
<property name="jar.dir"     value="${build.dir}/jar" />

<property name="main-class"  value="org.literateprograms.ciphers.Rot13" />

Finally, to make the build file as easy to use as possible, we provide two pseudo-targets. The first clean-build executes the clean target followed by the jar and has the effect performing a clean build:

<<pseudo targets>>=
<target name="clean-build" depends="clean,jar"/>

The final target main performs all the steps needed to perform a clean build, and test and run the program. As this target is identified as the default target in the project tag, it will be run if we provide no other options.

<<pseudo targets>>=
<target name="main" depends="clean,test,run" />

To use this build file with Ant simply extract a copy to the project directory and run ant. The result should be:

$ ant
Buildfile: build.xml

clean:
  [delete] Deleting directory f:\dev\temp\rot13\build

compile:
    [mkdir] Created dir: f:\dev\temp\rot13\build\classes
    [javac] Compiling 2 source files to f:\dev\temp\rot13\build\classes

jar:
    [mkdir] Created dir: f:\dev\temp\rot13\build\jar
      [jar] Building jar: f:\dev\temp\rot13\build\jar\rot13.jar

test:
Warning: Reference application has not been set at runtime, but was found during
build file parsing, attempting to resolve. Future versions of Ant may support
referencing ids defined in non-executed targets.
   [junit] Running org.literateprograms.ciphers.Rot13Test
   [junit] Tests run: 2, Failures: 0, Errors: 0, Time elapsed: 0.016 sec

run:
     [java] Usage: java -jar rot13.jar [-h] [-e "text to be encoded"] [< plaintext] [> ciphertext]
     [java] where arguments are:
     [java] -e encode text on the command line
     [java] -h print this message

main:

BUILD SUCCESSFUL
Total time: 2 seconds

You can also execute the individual targets as in:

$ ant clean 
$ ant test 
$ ant run

or even

$ ant clean test run

Summary

In this article we have presented the Rot13 algorithm and a program to implement it as a program suitable for use in a Command Line Interface (CLI) environment. We have also introduced the reader to unit testing in JUnit and the project build tool Apache Ant. Both JUnit and Ant are standard tools in the Java project developer's toolkit. The project file structure used here is also standard for Java projects. The project file structure and the Ant build file documented here can easily be adapted for other Java development projects. The references provide more details on both the Rot13 algorithm as well as the JUnit and Apache Ant tools.

See Also

References

  • Rot13, Wikipedia Article.
Download code
Personal tools