Skip to content

PA1: phrameworks

Phrameworks Logo

Learning Objectives

This assignment is designed to help you learn several things. First, it will help you learn about file input. Second, it will help you learn about exceptions. Finally, it will help you learn about specialization, inheritance, and polymorphism.

Overview

HomeRun is a (fictitious) company that designs and develops software and hardware for use in and around the home, especially products that help people run their households more easily. One such product is phrameworks.

HomeRun is developing phrameworks because they recognize that people are taking more photographs than ever before and that, while many photographs are posted on social networking sites of various kinds, people still want to be able to display their photographs around their homes. To satisfy that need, several companies, including HomeRun, manufacture and sell digital picture frames.

phrameworks are the next generation of digital picture frame. Unlike existing systems, phrameworks are equipped with sensors that can sense the temperature, humidity, sound waves, and light waves. In addition, phrameworks frames are aware of the date and time and any personal information the user wants to provide them with. Hence, phrameworks frames can display context-sensitive "show lists" (where a "show list" is like a "play list" for an MP3 player, but for photographs).

HomeRun has designed the first version of the software components of phrameworks and have come to you to implement those components. Because the hardware is not yet available, they have provided you with a software simulator that you can use during the implementation process. The relationships between the classes they want you to implement and the simulator are illustrated in the following UML class diagram (which can be enlarged by zooming-in on this page or by right-clicking and opening it in another window/tab).

Class Diagram

Classes in green were written by the GUI development team, classes in purple are part of the Java libraries, and classes in black are to be written by you for this assignment. Pay careful attention to the packaging in this UML diagram. In particular, note that HomeRun puts all of its main classes in the pa1.homerun package and organizes other classes based on their functionality.

Background Information

Before you can start working on the components, you need to learn a little bit about the format and naming of of Photo files.

The Naming of Photo Files

Photo file names start with a prefix and a number (that contains leading 0s), and end with ".txt". The prefix can be any valid String, the numbers start with 0, and the numbers end with the largest possible number of photos on the device (which will, when the hardware is completed, be determined by the amount of memory in the Phrame). So, for example, if the prefix is "Photo" and the largest Photo number is 9999, then the valid file names would be "Photo0000.txt", "Photo0001.txt", "Photo0002.txt", …, "Photo9999.txt". Note that all valid file names may not be used at any particular point in time. In other words, there can be gaps in the numbering.

The Format of Photo Files

Each Photo file contains one record that contains two or more fields, delimited using the green character in the figure below.

File Format

  1. The first field, which is always present, contains the name of the file containing the actual image.

  2. The second field, which is always present, contains a String representation of the Photo object's current rating (i.e., how much the person likes the Photo). A String that can't be converted into an Integer (e.g., "NA", "Unrated") indicates that the Photo has not been rated. Otherwise, the rating is guaranteed to be between 0 and 5 (inclusive).

  3. The remaining fields, if present, contain tag-value pairs (delimited using the red character in the figure above) with information about the Photo. Both the tag and the value may contain spaces, but are guaranteed not to contain any of the delimiters shown in the figure above.

In this example, field 0 contains the name of the image file (pic011.png), field 1 contains the rating (2), and fields 2 through 3 contain tag-value pairs. This Photo was tagged with two pieces of information – a description and a location. The values indicate that the Photo is of a street scene and that it was taken in Barcelona.

Twenty-three Photo files (named Photo00.txt to Photo22.txt), along with their corresponding images, have been made available to you for testing. They are available at:

data.zip

They contain enough variety for you to completely test your code. Of particular interest are: Photo00.txt which contains a rating of "NA", Photo02.txt which refers to a nonexistent image file, Photo07.txt which contains a rating of "Unrated", and Photo13.txt which contains a rating of "N/A".

Existing Components

The .class file for the Phrame class has been provided for you in the following .jar file:

pa1.jar

The source code for the Phrameworks class has been provided for you in the following .java file:

Phrameworks.java

Phrame

The hardware simulator, is encapsulated in the Phrame class. When it is constructed, it is passed an object that has all of the capabilities of a SequentialShowList. It uses that object to show a particular set of Photo objects. It's methods are shown in the UML class diagram.

Phrameworks

The Phrameworks class is the main class for the simulated product. You must not modify this file in any way. You are being provided with the source code to help you debug your code.

When executed, the main() method in the Phrameworks class must be passed either one, two or three parameters. It will always be passed a String representation of the delay between photos (in milliseconds). If there are exactly two arguments, then the second argument will be a String representation of the rating of interest. If there are exactly three arguments, then the second argument will be the tag of interest and the third argument will be the value of interest. The main() method then:

  1. Uses the PhotoReader class to read all (potentially numbered from 00 to 99) of the Photo objects that have the file names "Photo00.txt", "Photo01.txt", …, "Photo99.txt". (Note the leading 0s in the numbers that are less than 10.)

  2. Constructs the appropriate kind of SequentialShowList (based on the number of arguments);

  3. Constructs a Phrame that will use the SequentialShowList; and

  4. Makes the Phrame visible.

Components to be Written

You must write the Photo, PhotoReader, SequentialShowList, RatingShowList, and TagShowList components, as well as tests for all of these components.

Photo

A Photo is an encapsulation of a photograph and its properties. In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.

  1. tags and values must be conformal, and must contain the tag-value pairs.

  2. The constructors must initialize all of the attributes.

    1. They must read the BufferedImage that has the given file name (using the appropriate method in the Phrame class) and assign it to the appropriate attribute.
    2. They must assign the rating parameter to the appropriate attribute without doing any error-checking or validation.
    3. The four-parameter constructor must make the attributes named tags and values aliases for the corresponding parameters.
    4. The two-parameter constructor must make the attributes named tags and values both null. (Note: The two-parameter constructor must not duplicate any code in the four-parameter constructor.)
  3. The getTagValue() method must return null if the Photo object does not have a tag-value pair with the given tag. Otherwise, it must return the value for that tag (e.g., the value "Barcelona" for the tag "Location").

    1. It must return null if the given tag is null.
    2. It must return null if the tags attribute or the values attribute (or both) is null. 3. Think About. It must return all of the values that begin with the given letters if the tags attribute ends with a * character.

PhotoReader

The PhotoReader class can be used to read Photo objects from the file system. You must assume that the format of each file is correct, though, as discussed above, fields that are supposed to contain numeric values may not actually contain numeric values, and you must account for this.

  1. The digits() method returns the number of digits in an int. For example, when passed the number 5 this method must return 1 and when passed the number 1284 this method must return 4.

  2. The fileNameFormat() method returns a format String that can be used with the String.format() method to create a file name. For example, for a prefix of "Photo" and an n of 99, this method must return the format String "Photo%02d.txt". Then, invoking String.format("Photo%02d.txt", 8) would return "Photo08.txt" which is a potentially valid file name. (Note the leading 0.)

  3. The read() method is passed two parameters that are used to describe the file names. The first, named prefix, contains the characters in the file name that precede the number. The second, named n, contains the largest possible photo number. So, for example, if this method is passed "Photo" and 99 it means that the file names will potentially go from "Photo00.txt" to "Photo99.txt" (though the file system may contain fewer files than that).

    1. Possible file names must be read in Think About. reverse numerical order.

    2. If there is no file with a possible name or the BufferedImage associated with the Photo object can't be read, the read() method must not add that Photo to the ArrayList and continue on to the next file name.

    3. If the rating cannot be converted into a number, then the rating must be set to -1 (indicating that it has not been rated).Think About. If the rating is even it must be set to 0, if it is odd it must be set to 1.

RatingShowList

A RatingShowList is a "show list" that must repeatedly show Photo objects that have a rating that is greater than or equal to a target rating.

In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.

  1. The constructor must not perform any error-checking.

  2. The target rating passed to the constructor must be used to determine if the Photo object passed to the shouldShow() method results in a return value of true or false.

    1. The shouldShow() method must return false when photo is null.

    2. The shouldShow() method must return true when the Photo object's rating is greater than or equal to the target rating.Think About. The rating must be 10 more than the target rating for the method to return true.

      1. If the target rating is -1 then the shouldShow() method must only return true when the Photo object's rating is -1.Think About. Or non-negative).
  3. It must not contain any public, package, or protected methods methods that are not included in the UML class diagram.

TagShowList

A TagShowList is a "show list" that must repeatedly show Photo objects that have a target tag-value pair. In addition to the specifications contained in the UML class diagram, this class must conform to the following specifications.

  1. The targetTag and targetValue passed to the constructor must be used to determine if the Photo object passed to the shouldShow() method results in a return value of true or false.

    1. The shouldShow() method must return false when photo is null.

    2. The shouldShow() method must return false when the targetTag is null.

    3. The shouldShow() method must return false when the targetValue is null.

    4. The shouldShow() method must return false when the Photo object's value for the targetTag is null.

    5. Otherwise, the shouldShow() must return true when the Photo object's value for the targetTag equals the targetValue.Think About. It must return true only when the two are equal in the sense of the == operator..

  2. It must not contain any public, package, or protected methods methods that are not included in the UML class diagram

SequentialShowList

A SequentialShowList is a "show list" that repeatedly shows all of the Photo objects it contains, in order.

  1. The index attribute must be used to keep track of the next Photo to potentially be returned by the getNext() method (see below). How you do this is up to you.

  2. The constructor must make the photos attribute an alias of the photos collection that it is passed.

  3. The shouldShow() method must return false when the parameter is null, otherwise it must return true (since all Photo objects in a SequentialShowList should be shown).

  4. The getSize() method must return the number of elements in the photos collection that should be shown.

  5. The getNext() method must return the next Photo in the photos collection that should be shown.

    1. It must throw a NoSuchElementException (which is in the java.util package) when getSize() evaluates to 0.

    2. When getSize() does not evaluate to 0, it must always return the next Photo object.

      1. Calls to getNext() must repeatedly cycle through the photos collection (i.e., the index attribute must be reset after the last element is returned).
  6. The reset() method must reset the index attribute in such a way that the subsequent call to getNext() will behave exactly like the first call to getNext() (i.e., it must "reset" the showlist).

Testing

Some of the tasks that you must complete involve unit testing. You must use JUnit (v5) for this purpose.

Your JUnit test suite must cover all statements and all branches (as measured by JaCoCo) in all of the classes you must test. You should not include tests for the Phrameworks class. Your tests must be in a package named pa1.testing and each test class must include the word "Test" in its name.

Though 100% coverage is necessary, you should, by now, understand that it is not sufficient.

Note that JUnit tests need not conform to the course style guide.

Submission

You must submit (using Gradescope):

  1. Your implementation of the Photo, PhotoReader, SequentialShowList, RatingShowList, and TagShowList components (packaged appropriately).

  2. Your JUnit tests (packaged appropriately) for these classes.

You must submit a .zip file that contains the pa1 directory/folder at the top level. You must not submit the data files; they will be available on Gradescope. There is no limit on the number of submissions and no penalty for excessive submissions.

Grading

Your code will first be graded by Gradescope and then by the Professor. The grade you receive from Gradescope is the maximum grade that you can receive on the assignment

Gradescope Grading

Your code must compile and all class names and method signatures must comply with the specifications for you to receive any points on this assignment. Gradescope will then grade your submission as follows:

Criterion Points Details
Conformance to the Style Guide: 0 points (Success Required)
Passing your Tests (SelfTests): 10 points (All or Nothing; Success Required)
Coverage of your Tests (Coverage): 40 points (Partial Credit Possible)
Correctness (OfficialTests): 50 points (Partial Credit Possible)

Since you should now be pretty good at testing, Gradescope will only provide you with limited hints. In other words, your tests should uncover any defects in your code – you should not rely on the official tests to do so. In addition, since you should be using the coverage tool (JaCoCo) in VSCode, the coverage report you receive from Gradescope will be difficult to read. Specifically, it will include information about both covered and uncovered statements and branches. You should not use this report – you should use the coverage tool in VSCode since it is much easier to read.

Manual Grading

After the due date, the Professor may manually review your code. At this time, points may be deducted for inelegant code, inappropriate variable names, bad comments, insufficient tests, etc.

Since nobody will be looking over your shoulder, you can use any process that you would like to use. However, it is strongly recommended that you use the process describer here.

  1. Get Started

    1. Read and understand the entire assignment.

    2. Create a directory/folder for this assignment named pa1.

    3. Add pa1.jar to your workspace (i.e., to the lib directory/folder).

    4. Add the data files to your workspace. Specifically, download data.zip to a directory/folder outside of VSCode (e.g., the course downloads directory/folder). Then, unzip data.zip and copy all of the individual files (not the .zip file and not any directory that is created by unzipping) into the CS159 directory/folder (not the src directory/folder or pa1 directory/folder).

  2. Create the Packages

    1. Create the homerun, photo, showlist, and testing directories/folders under the pa1 directory/folder.
  3. Stub-Out All of the Classes

    1. Create a stub (i.e., a version that contains all of the methods with appropriate signatures and return values) of the Photo and PhotoReader classes in the pa1.photo package.

    2. Create a stub of the SequentialShowList, RatingsShowList, and TagShowList classes in the showlist package.

    3. Add the "javadoc" comments to the classes and methods.

    4. Check the style of the classes and make any necessary corrections.

    5. Add Phrameworks.java to the homerun directory/folder.

  4. Implement and Test

    1. Implement Photo

    2. Test (and debug, if necessary) the Photo class. Remember that your tests must be in the testing package and must be named appropriately. (Hint 1: The constructor of the Photo class declares that it throws an IOException. So, you will need to add a throws clause to the signature of your test methods.) (Hint 2: The getImage() method returns a reference to a BufferedImage object, so you won't be able to directly test that it is returning the correct reference. However, you can use the getWidth() and getHeight() methods in the BufferedImage class to ensure that the image has the correct dimensions. They are all 408x230.)

    3. Implement PhotoReader.

    4. Test (and debug, if necessary) PhotoReader. (Hint 1: In order to get 100% coverage, you sometimes have to be aware of some quirks of the language you are working in and the tool set you are using. More information about coverage of utility classes is available on the CS wiki.) (Hint 2: Your read() method will probably have a try-catch statement to deal with the possibility of an IOException. The file named Photo02.txt refers to an image that doesn't exist so that you can cover the catch block. You need not, indeed should not, worry about the situation in which the image both does not exist and has not been rated.)

    5. Implement SequentialShowList.

    6. Test SequentialShowList.

    7. Implement TagShowList.

    8. Test TagShowList.

    9. Implement RatingShowList.

    10. Test RatingShowList.

    11. Test the Complete Product

      1. Run the main class with two arguments in order to test its operation using a SequentialShowList.

      2. Run the main class with three arguments in order to test its operation using a RatingShowList. Perform this system test with several different ratings.

      3. Run the main class with four arguments in order to test its operation using a TagShowList. Perform this system test with several different tags and values (e.g., a "Location" of "Barcelona", a "View" of "Exterior").

Help

You may find the following helpful while completing this assignment.

Help with the PhotoReader Component

Note that, if you put the data files in the location described above, your PhotoReader class will be able to find them in the working directory (both on your machine and on the Gradescope server). So, you can (indeed, must) use only the file name without a path when creating a File object (or doing something like it). For example, you can create a File object using new File("Photo00.txt").

Note also that there is no reason to work with directories or directory listings. You can (and should) construct the names of the files within a loop using the fileNameFormat() method.

Finally, before you try and write the read() method in the PhotoReader class you may want to make sure that you can read and process a single .txt file. You may even want to write a simple main class (that won't become part of your final submission) that does this to make sure you understand all of the issues that arise before doing anything else.

Help with the SequentialShowList Component

Be careful to avoid an infinite loop when writing the getNext() method in SequentialShowList. You will need to loop until you find an element that should be shown, but you need to ensure that there is such an element (using the getSize() method) before you enter the loop.

Help Using Command Line Arguments in VSCode

As you (should know), the main() method in the main class is passed an array of String objects called command-line arguments. The main class Phrameworks.java uses these command-line arguments to control the operation of the application (see the comments in that class for details).

The easiest way to provide command-line arguments in VSCode is to:

  1. Click on the "TERMINAL" tab at the bottom of the window to make it active.
  2. Run the Phrameworks main class the usual way.
  3. The command that was used to run the program is displayed in the terminal. Click and drag to select that command and then copy it.
  4. Paste that command into the console, type a space, add any command-line arguments (delimited by spaces), and press [Enter].

Note, also, that the arrow keys will bring up a history of commands which makes it easier to repeat them.

Relevant Programming Patterns

An understanding of the following programming patterns will help you complete this assignment:

Questions to Think About

You don't have to submit your answers to these questions, but you should try to answer them because they will help you determine whether or not you understand some of the important concepts covered in this assignment.

  1. At some point, a PhotoReader must determine if the second field in the record it reads is a valid integer or not. Why is it a good idea to use exception handling for this purpose?

  2. Should the getSize() method in SequentialShowList perform the necessary calculations each time it is called, or should it just return an attribute that is initialized in the constructor? What are the advantages and disadvantages of both approaches?

  3. The getNext() method in SequentialShowList throws a NoSuchElementException when the list is "empty". It could, instead, return null. What are the advantages and disadvantages of both approaches?

  4. Why was a tab character used as the delimiter in Photo files rather than the space character?

  5. Why were the tag-value pairs delimited using a different character (in this case, a colon)?

  6. In the main() method of Phrameworks, the local variable named list is declared to be a SequentialShowList but is instantiated as either a RatingShowList, a TagShowList, or a SequentialShowList. Why do the assignment statements compile?

  7. Why is it better to declare list in the most general way possible (i.e., as a SequentialShowList)?

Looking Back - The Big Picture

By the time you complete this assignment you should have learned many things, including but not limited to the following “big picture” issues.

  • When you first encounter exceptions, they usually arise only when your code contains defects. You should now realize that exceptions are just an alternative return mechanism and that they can and should be used productively in a wide variety of circumstances.

  • Every time you design a class that has class-type attributes, you must decide whether to use aliases or deep copies (and how deep those copies should be). Sometimes it is better to use aliases and sometimes it is better to use deep copies. Each situation is different and requires careful thought.