PA1: phrameworks¶
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).
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.
-
The first field, which is always present, contains the name of the file containing the actual image.
-
The second field, which is always present, contains a
String
representation of thePhoto
object's current rating (i.e., how much the person likes thePhoto
). AString
that can't be converted into anInteger
(e.g.,"NA"
,"Unrated"
) indicates that thePhoto
has not been rated. Otherwise, the rating is guaranteed to be between0
and5
(inclusive). -
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:
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:
The source code for the Phrameworks
class has been provided for you in the
following .java
file:
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:
-
Uses the
PhotoReader
class to read all (potentially numbered from 00 to 99) of thePhoto
objects that have the file names"Photo00.txt"
,"Photo01.txt"
, …,"Photo99.txt"
. (Note the leading 0s in the numbers that are less than 10.) -
Constructs the appropriate kind of
SequentialShowList
(based on the number of arguments); -
Constructs a
Phrame
that will use theSequentialShowList
; and -
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.
-
tags
andvalues
must be conformal, and must contain the tag-value pairs. -
The constructors must initialize all of the attributes.
- They must read the
BufferedImage
that has the given file name (using the appropriate method in thePhrame
class) and assign it to the appropriate attribute. - They must assign the
rating
parameter to the appropriate attribute without doing any error-checking or validation. - The four-parameter constructor must make the attributes named
tags
andvalues
aliases for the corresponding parameters. - The two-parameter constructor must make the attributes named
tags
andvalues
bothnull
. (Note: The two-parameter constructor must not duplicate any code in the four-parameter constructor.)
- They must read the
-
The
getTagValue()
method must returnnull
if thePhoto
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"
).- It must return
null
if the given tag isnull
. - It must return
null
if thetags
attribute or thevalues
attribute (or both) isnull
. 3. Think About. It must return all of thevalues
that begin with the given letters if thetags
attribute ends with a*
character.
- It must return
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.
-
The
digits()
method returns the number of digits in anint
. For example, when passed the number5
this method must return1
and when passed the number1284
this method must return4
. -
The
fileNameFormat()
method returns a formatString
that can be used with theString.format()
method to create a file name. For example, for a prefix of"Photo"
and ann
of99
, this method must return the formatString
"Photo%02d.txt"
. Then, invokingString.format("Photo%02d.txt", 8)
would return"Photo08.txt"
which is a potentially valid file name. (Note the leading 0.) -
The
read()
method is passed two parameters that are used to describe the file names. The first, namedprefix
, contains the characters in the file name that precede the number. The second, namedn
, contains the largest possible photo number. So, for example, if this method is passed"Photo"
and99
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).-
Possible file names must be read in Think About. reverse numerical order.
-
If there is no file with a possible name or the
BufferedImage
associated with thePhoto
object can't be read, theread()
method must not add thatPhoto
to theArrayList
and continue on to the next file name. -
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.
-
The constructor must not perform any error-checking.
-
The
target
rating passed to the constructor must be used to determine if thePhoto
object passed to theshouldShow()
method results in a return value oftrue
orfalse
.-
The
shouldShow()
method must returnfalse
whenphoto
is null. -
The
shouldShow()
method must returntrue
when thePhoto
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 returntrue
.- If the target rating is -1 then the
shouldShow()
method must only returntrue
when thePhoto
object's rating is -1.Think About. Or non-negative).
- If the target rating is -1 then the
-
-
It must not contain any
public
,package
, orprotected
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.
-
The
targetTag
andtargetValue
passed to the constructor must be used to determine if thePhoto
object passed to theshouldShow()
method results in a return value oftrue
orfalse
.-
The
shouldShow()
method must returnfalse
whenphoto
isnull
. -
The
shouldShow()
method must returnfalse
when thetargetTag
isnull
. -
The
shouldShow()
method must returnfalse
when thetargetValue
isnull
. -
The
shouldShow()
method must returnfalse
when thePhoto
object's value for thetargetTag
isnull
. -
Otherwise, the
shouldShow()
must returntrue
when thePhoto
object's value for thetargetTag
equals thetargetValue
.Think About. It must returntrue
only when the two are equal in the sense of the==
operator..
-
-
It must not contain any
public
,package
, orprotected
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.
-
The
index
attribute must be used to keep track of the nextPhoto
to potentially be returned by thegetNext()
method (see below). How you do this is up to you. -
The constructor must make the
photos
attribute an alias of thephotos
collection that it is passed. -
The
shouldShow()
method must returnfalse
when the parameter isnull
, otherwise it must returntrue
(since allPhoto
objects in aSequentialShowList
should be shown). -
The
getSize()
method must return the number of elements in thephotos
collection that should be shown. -
The
getNext()
method must return the nextPhoto
in thephotos
collection that should be shown.-
It must throw a
NoSuchElementException
(which is in thejava.util
package) whengetSize()
evaluates to 0. -
When
getSize()
does not evaluate to0
, it must always return the nextPhoto
object.- Calls to
getNext()
must repeatedly cycle through thephotos
collection (i.e., theindex
attribute must be reset after the last element is returned).
- Calls to
-
-
The
reset()
method must reset the index attribute in such a way that the subsequent call togetNext()
will behave exactly like the first call togetNext()
(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):
-
Your implementation of the
Photo
,PhotoReader
,SequentialShowList
,RatingShowList
, andTagShowList
components (packaged appropriately). -
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.
Recommended Process¶
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.
-
Get Started
-
Read and understand the entire assignment.
-
Create a directory/folder for this assignment named
pa1
. -
Add
pa1.jar
to your workspace (i.e., to thelib
directory/folder). -
Add the data files to your workspace. Specifically, download
data.zip
to a directory/folder outside of VSCode (e.g., the coursedownloads
directory/folder). Then, unzipdata.zip
and copy all of the individual files (not the.zip
file and not any directory that is created by unzipping) into theCS159
directory/folder (not thesrc
directory/folder orpa1
directory/folder).
-
-
Create the Packages
- Create the
homerun
,photo
,showlist
, andtesting
directories/folders under thepa1
directory/folder.
- Create the
-
Stub-Out All of the Classes
-
Create a stub (i.e., a version that contains all of the methods with appropriate signatures and return values) of the
Photo
andPhotoReader
classes in thepa1.photo
package. -
Create a stub of the
SequentialShowList
,RatingsShowList
, andTagShowList
classes in theshowlist
package. -
Add the "javadoc" comments to the classes and methods.
-
Check the style of the classes and make any necessary corrections.
-
Add
Phrameworks.java
to thehomerun
directory/folder.
-
-
Implement and Test
-
Implement
Photo
-
Test (and debug, if necessary) the
Photo
class. Remember that your tests must be in thetesting
package and must be named appropriately. (Hint 1: The constructor of thePhoto
class declares that it throws anIOException
. So, you will need to add athrows
clause to the signature of your test methods.) (Hint 2: ThegetImage()
method returns a reference to aBufferedImage
object, so you won't be able to directly test that it is returning the correct reference. However, you can use thegetWidth()
andgetHeight()
methods in theBufferedImage
class to ensure that the image has the correct dimensions. They are all 408x230.) -
Implement
PhotoReader
. -
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: Yourread()
method will probably have atry
-catch
statement to deal with the possibility of anIOException
. The file namedPhoto02.txt
refers to an image that doesn't exist so that you can cover thecatch
block. You need not, indeed should not, worry about the situation in which the image both does not exist and has not been rated.) -
Implement
SequentialShowList
. -
Test
SequentialShowList
. -
Implement
TagShowList
. -
Test
TagShowList
. -
Implement
RatingShowList
. -
Test
RatingShowList
. -
Test the Complete Product
-
Run the main class with two arguments in order to test its operation using a
SequentialShowList
. -
Run the main class with three arguments in order to test its operation using a
RatingShowList
. Perform this system test with several different ratings. -
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:
- Click on the "TERMINAL" tab at the bottom of the window to make it active.
- Run the
Phrameworks
main class the usual way. - 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.
- 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.
-
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? -
Should the
getSize()
method inSequentialShowList
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? -
The
getNext()
method inSequentialShowList
throws aNoSuchElementException
when the list is "empty". It could, instead, returnnull
. What are the advantages and disadvantages of both approaches? -
Why was a tab character used as the delimiter in
Photo
files rather than the space character? -
Why were the tag-value pairs delimited using a different character (in this case, a colon)?
-
In the
main()
method ofPhrameworks
, the local variable namedlist
is declared to be aSequentialShowList
but is instantiated as either aRatingShowList
, aTagShowList
, or aSequentialShowList
. Why do the assignment statements compile? -
Why is it better to declare
list
in the most general way possible (i.e., as aSequentialShowList
)?
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.