CS 51 Test Program #2 - FacePamphlet1
Design Due: Wednesday, April 29, at beginning of lecture
Due: Wednesday, May 6 at 4 PM

Introduction

A test program is a laboratory that you complete on your own, without the help of others. It is a form of take-home exam. You may consult your text, your notes, your lab work, or our on-line examples and web pages, but use of any other source for code is forbidden. You may not discuss these problems with anyone aside from the course instructor. You may only ask the TAs for help with hardware problems or difficulties in retrieving your program from a disk or network.

For many years, computers have been used as a ubiquitous platform for communication. While email is perhaps still the most common medium for computer-based interaction, social networking applications (such as Facebook, Orkut, and MySpace) have gained immense popularity in recent years. In this vein, your job for this assignment is to create an application that keeps track of a simple social network.

What is a Social Network?

For those of you not already familiar with social networks, a social network, in the simplest sense, is a means of keeping track of a set of people (each of whom have a "profile" in the social network) and the relationships (usually involving friendship) between them. For example, let's consider a simple social network that contains four people's profiles: Alice, Bob, and Cathy, and Don. Say now that Alice is friends with both Bob and Don (in which case, we consider Bob and Don to automatically be friends of Alice, reciprocally). And Cathy is also a friend of Don. Graphically, we could draw this "network" as:

Here, each profile in the network is represented by a circle containing the name of the profile (more formally, such circles would be called "nodes") and a friendship relationship between two people (which, for our purposes, is always reciprocal) is shown as a line connecting two profiles of people who are considered friends.

Problem Description

For this assignment, you will create an application that keeps tracks of the information in such a simple social network. More specifically, your application will allow for user profiles to be added to, deleted from, or looked-up in the social network. Moreover, for each profile, you will keep track of the person's name associated with that profile, an optional image that the person may wish to display with his/her profile, an optional "current status" for the profile (which is basically just a String indicating what activity the owner of that profile is currently engaged in), and a list of friends for each profile.

The Program Operation

To see how the program works, we give an example of using the program to create a small social network. Initially, the social network starts out empty (i.e., it contains no profiles). You can check out the demo below.




Below is a snapshot of the program when it starts up.

Here is another snapshot after three names have been entered.

Along the NORTH border of the application, is a text field entitled Name, along with three buttons: Add, Delete, and Lookup. To create a new profile, the user would enter a name in the Name text field and click the Add button.

In this profile displayed above, we note five display elements of interest:

Making Changes to the Current Profile

The buttons and fields in the left margin of the window can be used to alter the current profile. The three buttons displayed are "Change Status", "Change Picture", and "Add Friend". The text fields above these buttons are used to enter the corresponding information. When the "Change Status" button is pressed or the return key is pressed after typing in the corresponding text field, the information in the text field will be displayed in the status portion of the canvas.

If the name of a valid image file (either jpeg or gif) is entered in the text field associated with the "Change Picture" button and then that button is clicked or the return key is pressed then the picture associated with that file will be associated with the current profile and will be displayed. If the file name is incorrect then a Problem Message is displayed explaining the issue.

If the text field associated with "Add Friend" is filled with the name of a person already entered in the system and either the "Add Friend" button is clicked or the return key is pressed then that name will be added to the "Friends" list on the canvas. If the name does not correspond to someone already entered in the system then a Problem Message should be displayed.

We will assume that friendships are symmetrical, so when a friend is added to the profile of an individual, that individual is added to the friend's friend list.

After each of these actions is successfully executed then the corresponding text field should be cleared. If there is an error, then you should leave the bad text in the field so that the user can correct it.

Looking Up and Deleting Profiles

The buttons at the top of the window are used for adding, deleting, and looking up profiles, based on the name typed in the field to the left of the buttons. A Problem Message is displayed if the "Delete" or "Lookup" button is pressed with a name that has not been entered in the system or if the "Add" button is pushed with a name that is already in the system. As usual, if the operation is successful then the text field should be cleared. If there is an error, then leave the text in the field.

When a profile is removed from the system, be sure to remove that person's name from all of the friends lists of other profiles.

Implementation

The FacePamphlet program should be broken down into the following four classes:

FacePamphletController
This is the main program class that is responsible for creating and displaying the user interface and for responding to user actions. While you will need to figure out most methods of the you need for this class, I highly recommend that you write the following private method:
        /** 
         * Displays nicely on the screen all of the information from the 
         * user profile.  If there is no image associated with the profile 
         * you should display blankFace.jpg. 
         */ 
        private void displayProfile(FacePamphletProfile profile)
    

You will find the method setImage from class VisibleImage helpful in displaying a profile, as it allows you to change the image that is showing without creating a brand new VisibleImage. (See the objectdraw library documentation for details.)

We will supply the code to layout the buttons and fields visible on the left and top edges of the window. You are responsible for everything on the canvas.

FacePamphletProfile
This class should encapsulate all the information for a single profile in the social network. Given a FacePamphletProfile object, you can find out that profile's name as well as getting and setting the associated image and status, as well as getting and manipulating (adding to and deleting from) the list of names of friends for that profile. You can use either an array or an ArrayList to hold the list of names (held as strings) of the friends. We suggest the following methods be supported, though you will likely also want to add some private methods as well:
        /** This method returns the name associated with the profile. */ 
        public String getName()
        } 
 
        /** 
         * This method returns the image associated with the profile. If there is no  
         * image associated with the profile, the method returns null.  
         */  
        public Image getImage()
 
        /** This method sets the image associated with the profile. */ 
        public void setImage(Image image)
 
        /** 
         * This method returns the status associated with the profile. If there is 
         * no status associated with the profile, the method returns the empty 
         * string (""). 
         */ 
        public String getStatus()
 
        /** This method sets the status associated with the profile. */ 
        public void setStatus(String status) 
 
        /** 
         * This method adds the named friend to this profile's list of friends. It 
         * returns true if the friend's name was not already in the list of friends 
         * for this profile (and the name is added to the list). The method returns 
         * false if the given friend name was already in the list of friends for 
         * this profile (in which case, the given friend name is not added to the 
         * list of friends a second time.) 
         */ 
        public boolean addFriend(String friend)
 
        /** 
         * This method removes the named friend from this profile's list of friends. 
         * It returns true if the friend's name was in the list of friends for this 
         * profile (and the name was removed from the list). The method returns 
         * false if the given friend name was not in the list of friends for this 
         * profile (in which case, the given friend name could not be removed.) 
         */ 
        public boolean removeFriend(String friend)
 
        /** 
         * This method returns an iterator over the list of friends associated with 
         * the profile. (Feel free to have it return an array if you prefer.) 
         */ 
        public ArrayList<String> getFriends()
  
        /**
         * returns true if otherProfile's name is same as this one's.
         */
        public boolean sameName(FacePamphletProfile otherProfile) { 

        /** 
         * This method returns a string representation of the profile. This string 
         * is of the form: "name (status): list of friends", where name and status 
         * are set accordingly and the list of friends is a comma separated list of 
         * the names of all of the friends in this profile. 
         *  
         * For example, in a profile with name "Alice" whose status is "coding" and 
         * who has friends Don, Chelsea, and Bob, this method would return the 
         * string: "Alice (coding): Don, Chelsea, Bob"
         * This method is not used in the final version, but is invaluable in 
         * debugging your program!
         */ 
        public String toString()
    
FacePamphletDatabase
This class keeps track of all the profiles in the FacePamphlet social network. Note that this class is completely separate from the user interface. It is responsible for managing profiles (adding, deleting, looking-up individual profiles), and includes the following methods:
        /**
         * This method adds the given profile to the database. If the name
         * associated with the profile is the same as an existing name in the
         * database, the existing profile is replaced by the new profile passed in.
         */
        public void addProfile(FacePamphletProfile profile)
 
        /** 
         * This method returns the profile associated with the given name in the 
         * database. If there is no profile in the database with the given name, the 
         * method returns null. 
         */ 
        public FacePamphletProfile getProfile(String name)
 
        /** 
         * This method removes the profile associated with the given name from the 
         * database. It also updates the list of friends of all other profiles in 
         * the database to make sure that this name is removed from the list of 
         * friends of any other profile. 
         *  
         * If there is no profile in the database with the given name, then the 
         * database is unchanged after calling this method. 
         */ 
        public void deleteProfile(String name) { 
 
        /** 
         * This method returns true if there is a profile in the database that has 
         * the given name. It returns false otherwise. 
         */ 
        public boolean containsProfile(String name)
    
PMap
This class manages a collection of elements of type FacePamphletProfile. It can be represented as an array (e.g. FacePamphletProfile[]) or as an ArrayList (e.g., ArrayList<FacePamphletProfile>. Which you use is up to you, but one is significantly easier than the other. Here are the methods that I found helpful:
        // Add a new profile to the collection
        public void put(FacePamphletProfile newProfile)
        
        // Return whether there is a profile with "name"
        public boolean containsName(String name) 
        
        // Return the profile with "name".  If there is none, return null
        public FacePamphletProfile get(String name)
        
        // Return the profile with "name" from the collection.  Do nothing if not there.
        public void remove(String name)
    

Implementation

Getting the GUI set up properly

The program is designed to be run in a window with width 1000 and height 400. Class FacePamphletController has a begin method that calls methods setTopPanel and setLeftPanel to set up the left and top edges of the display window. You will need to insert the code to get everything set up on the canvas. We have provided lots of constants that we used for locating things on the screen. You may use them or modify them as you see fit as long as you get a clean readable interface. Initally there are no profiles in the system.

Once you get the canvas set up properly. Write an action performed method to handle clicks on each of the buttons and "returns" in the appropriate text fields (except the "nameField") - printing out a simple descriptive message indicating what is in the corresponding text field when each is triggered. Design this method so that if the corresponding field is empty then no action is taken (i.e., no message is printed).

Implement the FacePamphletProfile class

This class should keep track of all relevant information for each profile, including the name, status, image, and list of friends. Figure out the appropriate default values for each of these when a person is first added to the system (when the user clicks the "Add" button). You can figure out how to hold the list of friends, but we recommend either a String array or ArrayList<String>.

The last method in the starter implementation of FacePamphletProfile is a toString method whose role is to return a human-readable representation of the data stored in the profile. The general form of the string returned by this method is:

    name (status): comma separated list of friend names

For example, if the variable profile contains the FacePamphletProfile data of a profile with name "Alice" whose status is "coding" and who has friends named Don, Chelsea, and Bob, then profile.toString() would return the string:

    "Alice (coding): Don, Chelsea, Bob"

The toString method will be very useful as you continue to develop and debug your program. Use it often to make sure that everything is as you expect it to be!

Implement the PMap class

This class will be very useful in building your database. Implement the methods specified in the starter class.

Notice that the methods in this class are pretty generic. In fact, they are very similar to those in a built-in Java library called Map in which objects can be looked up according to a "key" value (which for us would be the name associated with the profile). At any rate, you should build this class yourself, using either an array or an ArrayList to hold the elements. Consider adding a private method to find where in the data structure is the profile with a given name.

Test that all of the methods correctly before proceeding.

Implement the FacePamphletDatabase class

This class should use an instance variable of type PMap (see below) that actually holds all of the profiles. Most of the method bodies call a corresponding method of PMap to actually change the data structure.

Only deleteProfile does extra work by searching through the database to eliminate the name associated with the profile from all of the friends lists that contained it.

For efficiency, try to only look at the entries of the database that definitely contain the name. (How can you know which those are?)

To test this part of the program, you can add code to the FacePamphlet program so that it creates the FacePamphletDatabase and then change the code for the Add, Delete, and Lookup button handlers as follows:

Modifying profiles

Now you are almost ready to go back to working on FacePamphletController and implementing the functionality for the "Change Status", "Change Picture", and "Add Friend" buttons and text fields.

To accomplish this you will need to keep track of the current profile. When a new profile is added, it is set to be the current profile. If the user attempts to add a name that is already in the database then the corresponding profile is made the current profile, but no new profile is created.

When a profile is deleted, there is no longer a current profile (even if the current profile is different). If there is a profile matching the name then it is deleted and the name is also removed from all friend lists that contain it. (Think about how you can find these efficiently!)

When the user looks up a name, the current profile is set to be the corresponding profile, if one exists. If a matching profile does not exist then there will be no current profile.

At the beginning of the program, and whenever there is no matching profile, use blankFace.jpg as the source of the image. When there is no current profile, or if the status of the current profile has not been initialized, then the status should be set to "no current status".

When the user changes status, the picture, or adds a friend then it changes the current profile. If there is no current profile then a Problem Message is displayed suggesting that the user select a profile.

When the user chooses a new picture, the setImage(Image im) message can be sent to the VisibleImage. If the file name is incorrect, the setImage method will throw an exception. You should catch the exception and display a Problem Message explaining that the file does not exist.

If the selected operation succeeds then blank out the associated field. If there is an error, then leave the previous text so that the user can correct it.

Adding a friend is a little more complex. If there is a current profile, see if the name selected is in the database. If so, then add it to the list of friends (if it is already there, display a Problem Message explaining what happened and don't add it a second time.) If it wasn't already added it then the friendship should be made reciprical by adding the name in the current profile to the new friend's list of friends. If the name is not in the database, display a Problem Message explaining the problem. As usual, if there is no current profile, display a Problem Message suggesting that a profile be selected.

Changing to friend's profile

Add functionality so that if the user clicks on a name in a friend list then the current profile changes to the friend's profile (and is displayed properly). The contains method works fine for Text objects. If the friends are displayed in an array of Text objects, then this should be relatively straightforward, behaving much like pressing the "Lookup" button with the friend's name.

Specifying behavior

Here is a summary of what should happen with each operation. Recall that nothing should happen if the corresponding text area is blank!

Adding a Profile
When a new profile is being added you should see if a profile with that name already exists. If it does, you should display the existing profile and give the user the message "A profile with the name <name> already exists". If the profile does not already exist, you should display the newly created profile and give the user the message "New profile created".
Deleting a Profile
When a profile is being deleted you should see if a profile with that name exists. If it does, you should delete the profile, clear any existing profile from the display, and give the user the message "Profile of <name> deleted". If the profile does not exist, you should clear any existing profile from the display, and give the user the message "A profile with the name <name> does not exist".
Looking up a Profile
When a profile is being looked up you should see if a profile with that name exists. If it does, you should display the profile, and give the user the message "Displaying <name>". If the profile does not exist, you should clear any existing profile from the display, and give the user the message "A profile with the name <name> does not exist".
Changing Status
When the status for a profile is being changed, you should determine if there is a current profile. If no current profile exists, you should just give the user the message "Please select a profile to change status". If there is a current profile, you should update its status, redisplay the profile (to show the changed status), and give the user the message "Status updated to <status>".
Changing Picture
When the picture for a profile is being changed, you should determine if there is a current profile. If no current profile exists, you should just give the user the message "Please select a profile to change picture". If there is a current profile, you should see if the filename given for the picture contains a valid image, and if it does, you should add the image to the profile, redisplay the current profile (to show the new image), and give the user the message "Picture updated". If the given filename could not be opened, you should just give the user the message "Unable to open image file: <filename>". In that case, the image associated with the profile is unchanged.
Adding Friend
When a friend is being added to a profile, you should determine if there is a current profile. If no current profile exists, you should just give the user the message "Please select a profile to add friend". If there is a current profile, you should see if the given friend name is the name for a valid profile in the social network. If the name is valid and the current profile does not already have that person as a friend, then you should update the friend list for both the current profile and the named friend, redisplay the current profile (to show the addition of the friend), and give the user the message "<friend name> added as a friend". If the named friend is already a friend of the current profile, you should just display the message "<name of current profile> already has <friend name> as a friend." If the named friend does not have a profile in the social network, then you should simply display the message "<friend name> does not exist." If an attempt is made to add the name of the current profile as a friend, display the message "You can't be your own friend!".
Changing to Friend's profile
When the user clicks on the name of a friend in the current profile then the current profile becomes that corresponding to the friend's name. You should display the profile, and give the user the message "Displaying <name>".

Advanced Functionality

Loading and Saving the database (5 pts)

This program becomes much more interesting if you can actually save the database. Add a text field labelled "File" as well as "Load" and "Save" buttons to the window. You can add these to either the top or the bottom of the window. The "Load" and "Save" buttons should only have an effect if the text field is non-empty. Otherwise the buttons should just be ignored.

When the user clicks the "Save" button, a file should be written with the following format:

    Profile name
    Name of image file for profile (a blank line if no image is set)
    Status of profile (a blank line if no profile is set)
    Names of friends, one name per line, if any
    A final blank line to separate it from info on the next profile

Thus each profile will be represented by 3+n lines, where n is the number of friends, and then followed by a blank line.

When the user clicks on the "Load" button, the current database will be flushed and information from the file name will be loaded into the program. You may assume that the file format is correct and that all of the image files actually exist in the directory.

Be sure to import java.io.* so that all of the file classes and methods will be found. If there is an error reading or writing the file, display a Problem Message to that effect.

Other extra credit

Here are some additional ideas for ways to extend your FacePamphlet program:

Academic Honesty Reminder

This test program should be treated like an open-book take-home examination, as described in the Academic Honesty Guidelines handed out at the beginning of the semester and on the web page. You may consult your text, your notes, your lab work, or the code provided in human readable form in our on-line examples, but use of any other source for code is forbidden. Any questions you have regarding this assignment or the particulars of your program should be addressed only to an instructor. The TA's can only answer questions involving incorrect functioning of your computer or Java compiler. When in doubt, they will refer you to an instructor.

Submitting Your Work

Please turn in a printed copy of your design document (worth 10% of the grade) at the beginning of your lecture section on Wednesday, April 29. To get full credit, the design document should include all of the basic program features. However, partial credit will be given for designs that include fewer features. The final program is due at 4 PM on Wednesday, May 6. You should turn in the program as usual by creating a folder that includes your name and the phrase "Test Program 2" and depositing it in the dropoff folder for the course.

Good luck and have fun!

Grading Point Allocations

Value

Feature
Design preparation (10 points total)
2 points Variable and constant names and types
2 points Method signatures
2 points English descriptions
2 points Pseudocode
2 points Plausibility
Code Quality (38 points total)
6 points Descriptive and helpful comments
2 points Good names
2 points Good use of constants
2 points Appropriate formatting
2 points Appropriate use of public/private qualifiers
4 points Booleans, conditionals, and loops
10 points Parameters, variables, and scoping
10 points Appropriate public/private methods for each class
Efficiency (4 points)
Basic Functionality (40 points total)
4 points Display of an empty profile
6 points Adding and displaying a profile
6 points Deleting a profile
4 points Lookup a profile
4 points Update status
6 points Update picture
6 points Add friend (including reciprocally)
4 points Clicking on friend switches current profile
Advanced Functionality (maximum of 10 points total)
5 points Loading and saving the database
1-2 points Extra profile elements
2 points Support for communities
2+ points Display friendship chains
?? points check with us if you have other ideas for advanced features