Lab 4: O-O Review and Intro to Revision Control

Note: for your convenience, this lab reproduces much of the material from the pre-lab reading.

The goal of this lab is to practice object-oriented design on a problem that will help you review the object-oriented features of Java and prepare for an exploration of object-oriented design techniques. This lab will also introduce the use of Mercurial, a revision control system.

1. Introduction

Object-oriented design is concerned with the relationship among classes and other types—which classes implement which interfaces, which classes serve as components to other classes (by means of instance variables), and (as we will learn about soon) which classes share code by inheritance or composition.

You will practice doing object-oriented design (and think about how revision control works) by designing your own simple revision control system. You will write a program that presents the user with a simple text area to edit. The user can "commit" changes made to the text area; these changes are saved in memory and assigned a version number; and the user can then go back to them later.

The user interface is a window that looks like this:

The "current" and "max" indicator at the top should display the version number of the currently displayed version (besides any changes that have been made since the last commit or recall) and the highest version number that has been assigned, respectively. When the program starts up, these should read "Current: 0" and "Max: 0", not "Current" and "Max" as the image shows.

When the user enters some text and then presses the "commit" button, the current and max counters should increment. Suppose the user then enters more text and presses "commit" again. If the user then presses the "<" button, the text should then revert to its state when the "commit" button was pressed the first time. Pressing the ">" button will change the text to what it was when "commit" was pressed the second time. In this way the user can shuffle though versions. Pressing "<" when the current indicator is 0 does nothing, and pressing ">" when the current indicator is equal to the max does nothing.

That specification would be adequate (and the problem very easy) if all the revisions were made in a linear fashion, and the user commited changes made only on the latest version-- in which case we would have an organization like

But what should happen if the user flips back to an earlier version, makes changes, and commits? Should the new version simply be put at the end of the chain, even though it does not descend from the latest version? Should the new version replace the version that used to come after the version that was edited, and the rest of the chain be lost?

Instead of the two options mentioned, your program should consider the series of versions to branch at this point. The new version will receive the next verions number (ie, the max version plus one), but instead of conceptually coming after the most recent version, it will be considered to branch off from the version it was derived from.

Suppose the user takes the collection of versions illustrated above, and then navigates back to version 3, makes a change, and commits (creating version 6). Then he or she makes another change (to the new version 6) and commits it as version 7. Then he or she navigates to 4, changes and commits, navigates back to 6 and makes to changes in a row, and navigates to version 5 to make and commit a final change. That would result in the following tree of versions.

At this point, your version navigation feature should work as follows: When at version 6, pressing the "<" button will bring up version 3, the parent. Pressing ">" (at version 6) would bring up verion 9, the most recent decendant. To move among "sibling" versions, the user would use the "-" button; so, pressing the "-" button at version 6 would bring up version 4, and pressing "-" again would bring back version 6. If there are more than two siblings, the "-" should move from the most recent to the oldest, in a circular manner.

As you fill out the program, you should build a linked data structure that corresponds to the tree. At each stage, think about the links that you will need to add.

2. Set up

To make a new directory for this lab, clone the provided Mercurial repository with the command

hg clone /cslab/class/csci245/lab4

If you use ls, you'll see that you have a new directory named lab4.

Move into that directory and take a look around.

3. Inspecting the code

You will find four sets of classes:

Look at the file VCWindow.java. This interface describes how to interact with the window that the user sees. ConcreteVCWindow is a class that implements this interface, and it has a no-parameter constructor. ConcreteVCWindow$SuperButton is a helper class. You do not need to look at the source code for these: everything you need to know is in the VCWindow interface.

Look at the file VersionControl. You'll see it has a main method which makes a new window and a new controller. It also creates action listeners for the buttons.

Look at one of the action listener classes (they're all about the same). You'll see they simply call an appropriate method on the controller object.

Finally, look at Controller.java. This is a class you will modify. This defines the object that needs to keep track of the various versions and update the window appropriately.

Compile and run VersionControl.java. You'll see a window appears on the screen, and you can type in it, but nothing happens when you click on the buttons. You'll be implementing the features of the buttons as you go along.

4. Step 1: The commit feature

The first feature you will implement in the system is the ability to commit a version. When the user clicks the commit button, the text currently on the textfield should be stored (in computer memory; we won't be doing file I/O in this lab), that version should be given a new version number, and the "max" and "current" version indicators should be incremented.

Write a class to represent versions of the text. Think about what information would need to be stored in a version object, and write a constructor. Don't worry about methods yet, you'll write those as we go along. You'll also be adding more instance variables as you fill in methods.

Controller will need an instance variable to refer to the current version and it will need a counter to keep track of the latest version number. Finally, fill in the commit() method in Controller so that it makes a new version and updates the indicators on the window.

Test to make sure this is working. Because you have reached a good point in your work, you should commit your work to your repository. In a terminal window, cd into your lab4 directory. Now run

hg status
If you named your new class QuuxClass.java (which I really hope you didn't), you should see something like
M VersionControl.java
? QuuxClass.java
which indicates that you have modified the file VersionControl.java and created a new file since the last commit. You need to tell Mercurial about your new file (substitute the sensible name you chose):
hg add QuuxClass.java
Now hg status will show
M VersionControl.java
A QuuxClass.java
with the A indicated that the file is added. You will always check status before you do an actual commit, just to make sure that you haven't missed something. Now run
hg commit -m "Completed commit method."
That adds a new revision to the repository. If you run
hg status
again, you should see that there are no changes. You can run
hg log
to see the history of the repository, which now includes your revision, described by the message you supplied above.

5. Step 2: The "previous" feature

Now that the program has the ability to label the versions, we want to be able to retrieve an earlier version. Pressing the "previous" button should bring back the version that the current version was based on.

What information should your version class have in order for that to be retrieved? What method would the class need so that the controller can retrieve the previous version?

Implement this change in your version class and in the previous() method in Controller.java. Make sure all the instance variables of your version class are private.

If the user tries to move to the "previous" version of version 1, nothing should happen (but the program shouldn't crash). Also, make sure that if the user navigates to an older version, edits, and commits, that the new version is given the next version number and is hooked properly into the chain of versions. At this point in development, it's ok if the later versions get lost; you'll fix that a bit later.

Make sure that this feature works. It would be a good idea to commit your changes to Mercurial before moving to the next step.

6. Step 3: The "next" feature

Now update your version class and fill in the next() method of Controller so that we can move to the next version.

Again, it is a probably a good idea to commit to Mercurial.

7. Step 4: The "parallel" feature

Now update the program so that if someone edits and commits based on an earlier version, the later versions are no longer lost, and the "-" button can be used to navigate among siblings, as described above.

8. Time permitting: Step 5: The "go to" feature

Update the program so that the user can jump directly to a version by giving its number. (Hint: use an ArrayList.)

9. Handing in

Once you are done, be sure that everything is committed to your Mercurial repository. Then, from your lab4 directory, do

/cslab/class/csci245/handin lab4 .
Once again, the dot on the end of the command says to turn in your entire directory.

10. Copying the files to your account

You could copy the entire folder to your account using the same method as in the previous lab. But you can also use Mercurial to clone the repository. If your username were mortimer.snerd and you already had a csci245 directory in your home directoy, you could the command

hg clone . ssh://msnerd@cslab25/csci235/lab4
That will prompt you for your password, twice.

When you log in to your own account later, it will look like your lab4 directory is empty. Run the command

hg update
there to get the latest revision checked out into the working copy.

You'll find a link to more information about working with Mercurial on the class page.


Thomas VanDrunen, Cary Gray
Last modified: Thu Feb 7 11:37:16 CST 2013