Overview: Work in pairs, both together and separately

In this lab we'll write a non-trivial sized program in Java, but it will be written following the procedural programming paradigm — not the way one normally programs in Java. However, we'll get familiar with how Java language features, run-time features and conventions enable and even encourage collaboration. We'll also git a little familiarity with a tool that makes collaboration better still — the "git" version control system.

You'll work in pairs on a program to do "Mad Libs". Sometimes you'll be working together at a single computer, sometimes separately on different parts of the same project. At all times, you'll be constantly testing and debugging as you go. At the end, you should be well-positioned to start learning the core or this class: Objected-Oriented Programming.

Step 0: Set up git profile

We're going to use a tool called "git". To use it you need to setup at least a minimal profile in your home directory. Give this command to make that happen.
git config --global user.email `whoami`@usna.edu
Also, you're going to be sharing some files between one another. To make this possible, you need to give the command
chmod a+x ~/
This doesn't give anyone access to any of your files, but it is required in order for you to later give access to one of your files.

Step 1: Linked lists

Working together with a partner (at one computer), create a file with the following class definition:
public class StringNode
{
  String data;
  StringNode next;
}
You will use this class as the basis for some functions manipulating linked lists of strings. Next create a class ListStuff that defines the following methods/functions (you must fill in the { ... } with the code for the function bodies):
// addToFront(s,Nold) returns a StringNode reference representing the list obtained by adding s to the front of list Nold
public static StringNode addToFront(String s, StringNode Nold) { ... }

// listToArray(N) returns a reference to an array containing the same strings as in the list N (in order)
public static String[] listToArray(StringNode N) { ... }
You must define a main method/function to test the code with (of course!), and you may add any other methods/functions you find useful.

Note: A "list" is now represented by a StringNode reference. The "empty list" is represented by a null reference. So, for example:

StringNode N = null;      // at this point N *is* an empty list
N = addToFront("rat",N);  // at this point N *is* the list ("rat")
N = addToFront("dog",N);  // at this point N *is* the list ("dog","rat")
N = addToFront("pig",N);  // at this point N *is* the list ("pig","dog","rat")
String[] A = listToArray(N);
for(int i = 0; i < A.length; i++)
  System.out.println(A[i]);
... should print out this when it's done:
pig
dog
rat

Step 2: Collaborating with a git repository

Working together on a program sometimes means two or more folks huddled around one keyboard, but often it means doing a little planning at a whiteboard, then going off to your separate comptuers to work on different components of your program. This kind of collaboration is greatly enhanced by using version control systems. These utilities manage pulling updated code from colleagues, pushing the updates you make to the code so others get it, and tracking changes to projects over time.

We're going to use a program called "git" to do this. In order to use it as we will be doing, you and your partner will need a shared directory — i.e. a directory in one or the other of your accounts that both of you can read from and write to. I've set up a script ~wcbrown/bin/cher to take care of that for you. In the shared directory, you will create a git "repository". This is a central repository of all the files you and your partner will be creating and working on. Each of you will checkout ("clone" in git parlance) the repository in a convenient place in your own home directories, and periodically "push" the changes you've made to the repository, and "pull" the changes your partner has made from the repository.

Here's how we'll setup the shared repository and get some files checked into it. On the account of the person who's computer you've been working at:

  1. In a separate terminal window from the work you've already done, give the following commands:
    cd ~                                ← cd to your home directory
    mkdir share02                       ← create new directory "share02"
    cd share02                          ← cd to the new directory
    ~wcbrown/bin/cher m17xxxx m17xxxx   ← use the alpha codes of the two partners.  This makes the directory "shared"
  2. In the same directory as the previous step, give the command:
    ~wcbrown/bin/git-init
    This will create a git repository that you both can share. Note the output line that looks like
    checkout the repo with: git clone ssh://mich302csd17u.academy.usna.edu/home/mids/m17xxxx/share02
    Save that! That's what you need to do to "checkout" or "clone" a copy of the repository.
  3. Now return to the directory you wrote the Step 1 code in, and give your version of the command:
    git clone ssh://mich302csd17u.academy.usna.edu/home/mids/m17xxxx/share02
    if you do an ls, you should see the directory share02 has suddenly appeared. In it, you should find a README file. Copy your .java files into the share02 directory, for example with the command cp *.java share02 (assuming you're still in your lab02 directory). Cd to share02 and make sure it's all there.
  4. Finally, let's check your code into the repository:
    git add StringNode.java ListStuff.java
    git commit -m "Initial commit of linked list code."
    git push
    The "git add ." line says that all the files currently in the directory are ones you want subject to version control. The "git commit ..." line commits the new files to your own local clone of the repository. Finally, the "git push" pushes the new stuff that's in your clone to the repository.
  5. Now, the "other" partner should go to his/her computer, cd to the appropriate ic211 directory, and give your pair's version of the command:
    git clone ssh://mich302csd17u.academy.usna.edu/home/mids/m17xxxx/share02
    A simple ls should let you know whether you got the updated version.
At this point, you'll go work separately on new class files. Whenever you feel you've finished something, add/commit/push like this
git add Foo.java bar.txt ← assuming Foo.java and bar.txt are files you've modified and want to commit
git commit -m "brief description of what you've done"
git push
... and when your partner has pushed something important, pull that new stuff like this:
git pull
At any time you can give the command
git status
to see what files have changed since your last commit/pull. Also, if you want to see exactly what's changed in a file you've modified, say README, for example, you can do
git diff README
... and git will show you (in "diff" syntax) what's changed.
To test this out, have Partner 1 edit README to include the names and alphas of both Partner 1 and Partner 2.
Partner 1 does:
git add README
git commmit -m "Added names and alphas"  
git push
... then ...
Partner 2 does:
git pull
At this point, if Partner 2 cat's the contents of README, you should see the "new" version with names and alphas.

Step 3: Go your own way

Partner 1
You will create a class WordRead that defines a function
public static String[] get(String fname) { ... }
that takes a filename fname and reads all the strings in that file, returning them (in order) in an array. Since you don't know how many strings are in the file, you're going to have to use a linked list (good thing you have one!) to read and store, then copy into an array. Unfortunately, you really need to have things stored in order!

Of course you'll have to thoroughly test this by writing a "main" for your WordRead class. For input file nouns.txt your test program should simply call get("nouns.txt") and print the resulting array, showing the words in nouns.txt one per line.

One thing you'll need to be able to do is create a Scanner that reads from a file. Without understanding what's going on, here's how to do that:

Scanner sc = null;
try { sc = new Scanner(new FileReader(fname)); } 
catch(IOException e) { e.printStackTrace(); System.exit(1); }
You'll need a "import java.io.*;" for that. I also suggest you take a look at the Scanner class's hasNext() (see the Scanner API documentation). The gist is that sc.hasNext() returns true if there's a "next" string to be read, and false otherwise. This will tell you when to stop reading from the input file.

Partner 2
You'll create a class named Formatter and define in it a function
public static void writeInColumns(String[] A, int cols) { .. }
that takes an array A of strings and a positive int "cols" and prints the strings from A out, in order, separated by spaces, but never using more than "cols" characters (including spaces!) in a line. So you have to periodically insert newlines as you go.

Of course you'll have to thoroughly test this by writing a "main" for your Formatter class. Here's a sample of how writeInColumns works for an array containing the strings: These, are, the, times, that, try, men's, souls.

cols = 20
These are the times 
that try men's
souls. 

cols = 10
These are 
the times 
that try 
men's 
souls.
Note: Test this thoroughly!

Step 4: Combining your work

At this point, make sure you've both pushed and pulled so that both have the up-to-date repository. Then, on one computer, continue development by creating a class MidLibs that defines a program that, at this point, takes a filename and prints the words in that file within 35 columns. The input filename will come as a command-line argument. The way this works is simple. In running a program like this:
java ClassName foo bar 23
The String[] args that is the parameter to main is the array {"foo","bar","23"}. If there isn't a filename (args has length 0), you should print out a usage message and exit. System.exit(0) will do this for you. Download the file madin01.txt. Here's some sample runs for the program.
~/$ java MidLibs
usage: java MidLibs <filename>
~/$ java MidLibs madin01.txt
One of my @nounp came to see me in
my office. He was @adjective with
his @nounp . He asked if there were
any optional assignments he could
do to @verb things. I told him that
I don't believe in @nounp , but
that I was happy to help him @verb
for the final.

Step 5: [Going Further] MidLibs

The last step is to replace each @noun, @verb, @adjective and @nounp (plural noun) with a randomly selected word. You read the candidate words in from the files nouns.txt, verbs.txt, adjectives.txt. For a plural noun you can simply add an "s" to the end of a randomly chosen noun. Now you have a program that really does Mad Libs!

Note: The == sign cannot be used to test for equality between String objects. It tests for equal references, not strings. Instead, if A and B are Strings, you test for equality with
A.equals(B)
... which returns true if the strings are equal, and false otherwise.