Programming Assignments
2-week Assignments
4 programming assignments will be assigned during the semester. Note that their due dates are subject to change.
- Programming Assignment 1 - Due:
10/22 11:00PM (Extended to 10/25 11:00PM) (I/O and Specialization) - Programming Assignment 2 - Due: 11/5 11:00PM (Interfaces, Abstract Classes, and Polymorphism)
- Programming Assignment 3 - Due:
11/19 11:00PM (Extended to 11/26 11:00PM) (Recursion) - Programming Assignment 4 - Due: 12/10 11:00PM (Collections)
1 - PA4: BatchGeo
Programming Assignment 4
![BatchGeo](BatchGeo_Logo.png)
Learning Objectives
This assignment is designed to help you learn several things. First,
it will help you learn how to use type-safe collections. Second, it
will help you learn about the capabilities of different kinds of
collections (and when each is appropriate). Third, it will help you
learn more about file I/O. Finally, it well help you understand the importance of
testing and debugging.
Overview
Nearby is a (fictitious) company that develops software in three
closely related areas: personal navigation systems, en-route and
mobile commerce, and location-based services. You have been contracted
to develop the classes they will need to complete an application named
BatchGeo.
BatchGeo is an application that can be used to geocode
street addresses (i.e., convert street addresses to longitude/latitude
coordinates).
Background
Before you can start implementing the system, you need to learn a
little bit about the terminology and file formats used by Nearby.
Acronyms
- TIGER
- Topologically Integrated Geographic Encoding and Referencing
Definitions
- Address
- A street name and a house/lot number.
- Equator
- The imaginary line on the surface of and encircling the Earth that
is equidistant to the North and South Poles.
- High End of the Segment
- The end of the segment with the largest house/lot number
- House/Lot Number
- A number (for our purposes, an integer) ascribed to a particular
location (often a vacant lot, house, or business) on a segment.
- Interpolation Parameter for a House Number
- Given a low house number, L, a high house number, H, and a house
number of interest, A, in [L,H], the interpolation parameter for
A is defined as m=(A-L)/(H-L), where the house numbers are
treated like real numbers (not integers).
- Latitude
- The angular distance (in degrees) north (positive) or south
(negative) of the Equator.
- Line of Latitude (Parallel)
- An imaginary line on the surface of and encircling the Earth drawn
parallel to the Equator.
- Line of Longitude (Meridian)
- An imaginary great circle on the surface of the Earth connecting the
North and South Poles and perpendicular to the Equator.
- Linear Interpolation of Two Numbers
- Given two numbers, a and b, and an interpolation parameter, m ∈ [0,1],
the linear interpolation of a and b is denoted by v and defined as
v=(1-m) ⋅ a + m ⋅ b.
- Linear Interpolation of Two Points
- Given two points, a = < ax, ay > and
b = < bx, by >, and an interpolation parameter,
m ∈ [0,1], the linear interpolation of a and b is denoted by v
and defined as v = < vx, vy > where
vx=(1-m) ⋅ ax + m ⋅ bx
and
vy=(1-m) ⋅ ay + m ⋅ by
.
- Location
- A point in which the coordinates are a longitude and latitude.
- Longitude
- The angular distance (in degrees) east (negative) or west (positive)
of the Prime (Greenwich) Meridian.
- Low End of the Segment
- The end of the segment with the smallest house/lot number.
- Point
- A (2-dimensional) point is an ordered pair. The point a is typically
written as the ordered pair < ax, ay >
- Prime (Greenwich) Meridian
- The line of longitude designated to be at 0 degrees.
- Segment
- A portion of a street. For example, Main Street might consist of
several segments, one from Oak Street to Elm Street, one from Elm
Street to Pine Street, and one from Pine Street to Broad Street. A
segment has a low end house/lot number and high end house/lot number.
- Segment Base
- A “database” (i.e., group or collection) containing all
of the segments of interest.
- Street
- A named roadway that consists of one or more segments (that define
the street). For example, Main Street might consist of several
segments, one from Oak Street to Elm Street, one from Elm Street to
Pine Street, and one from Pine Street to Broad Street. A street has a
name (e.g., “Main Street”).
- Street Base
- A “database” (i.e., group or collection) containing all
of the streets of interest.
Data Files
The data files used in BatchGeo are “human readable” text files. Each
line in a data file is called a record, and each record may contain
one or more fields (which are, conceptually, columns or components of
the record). Fields in a record are separated by a delimiter
character.
BatchGeo currently uses two kinds of data files, .seg
files and .str
files, each of which is described below. A geographic region will consist
of two files that have the same name and these two extensions. So, for example,
the data files for the JMU area are named jmuarea.seg
and jmuarea.str
.
.seg
Files
.seg
files contain information about segments, as they are defined by
the U.S. Census Bureau’s TIGER system (though using a slightly
different format).
Records in .seg
files use the tab character (i.e., '\t'
) as the delimiter
.seg
files contain a variable number of records
.seg
files are terminated by an end-of-stream (sometimes called end-of-file) character
Each record contains nine fields as follows:
- The segment ID (
String
) - The longitude at the low end of the segment (
double
) - The latitude at the low end of the segment (
double
) - The longitude at the high end of the segment (
double
) - The latitude at the high end of the segment (
double
) - The length in KM (
double
) - The TIGER road type code (
String
) - The house/lot number at the low end of the segment (
int
) - The house/lot number at the high end of the segment (
int
)
An Example .seg
File Containing Seven Segments
![Example .seg File](seg_Example.png)
A Description of One Record in a .seg
File
![Description of a .seg Record](seg_Description.png)
.str
Files
.str
files contain information about streets.
Records in .str
files use the tab character (i.e., '\t'
) as the delimiter (Note: A tab character is used because street names can contain commas)
.str
files contain a variable number of records
.str
files are terminated by an end-of-stream (sometimes called an end-of-file) character
.str
files contain two kinds of records, name records and component records
Each name record contains two fields as follows:
- The street name (
String
) - The number of segments in the street (
int
)
Each component record contains one field, the segment ID (String
)
An Example .str
File Containing Four Streets
![Example .str File](str_Example.png)
A Description of One Street in a .str
File
![Description of a .str Street](str_Description.png)
A Geocoding Example
Using the example data files above, we can geocode the address 1970
Creek Ct (which consists of the int
house number 1970
and the street
name "Creek Ct"
).
From the .str
file we see that Creek Ct consists of a single segment
with ID 75739596. From the .seg
file we see that the low end number
on this segment is 1900 and the high end number on this segment is
1999. So, treating the house numbers as double
values (not int
values),
the interpolation parameter for house/lot number 1970 is given
by: m = (1970 - 1900) / (1999 - 1900) = 70/99 ≈ 0.707070
From the .seg
file we see that the low end location is <-78.737162,
+38.380782> and the high end location is <-78.736349,
+38.381549>. Using m to interpolate between these two points yields:
(1.0 - 0.707070) · -78.73716 + 0.707070 · -78.736349
= 0.292930 · -78.73716 + 0.707070 · -78.736349
= -23.064476 + -55.672110
= -78.736586
and
(1.0 - 0.707070) · 38.380782 + 0.707070 · -38.381549
= 0.292930 · 38.380782 + 0.707070 · 38.381549
= 11.242882 + 27.138442
= 38.381324
So, 1970 Creek Ct is geocoded to <-78.736586, 38.381324>, where, using
the Nearby convention, the longitude is listed before the latitude.
Note that, in this example, the interpolation parameter was truncated
at 0.707070. So, you may get slightly different answers if you use
more digits of accuracy. Your code must use the full accuracy of double
values.
The Components to be Written
The components to be written are illustrated in black in the following UML
class diagram. (The components in jade green are part of the Java API.)
![Class Diagram](Design.png)
Note that attributes and methods that are inherited are not made
explicit in this diagram. If a method appears in both a base class and
a derived class it means that the method in the derived class
overrides the method in the base class. Note also that methods that
are in an interface are not made explicit in classes that realize/implement
that interface.
In addition to the specifications in the UML class diagram,
your classes must conform to the following specifications.
The Location
Class
The toString()
method must return the longitude and latitude formatted using
a format String
of "%+9.6f,%+9.6f"
.
The OnSegmentLocation
Class
An OnSegmentLocation
is a Location
that knows the ID of the Segment
it is on.
This class implements the Comparable
interface so that the sort()
method
in the Collections
class can be used to sort collections of such objects based
on their segment IDs (which are String
objects that, themselves, implement the
Comparable
interface).
The Segment
Class
The default constructor must create a Segment
object with default
values for all of the attributes.
The contains()
method must return true
if the owning Segment
contains the given house number, and must return false
otherwise.
The interpolate()
method must geocode the given house number
using linear interpolation. (Hint: Be careful to use the longitude/latitude
for the low end of the segment as a and the longitude/latitude of the
high end of the segemnt as b.) It must return null
if the Segment
does not
contain the house/lot number.
the fromTSV()
method must set the value of all of the attributes
from a tab-separated-value representation (as in .seg
files). It
may need to throw one or more exceptions (e.g., if the String
has the wrong
number of fields, if a numeric field is non-numeric, etc.). It must return the ID of the segment.
The Street
Class
The constructor is passed the name of the Street
and must allocate memory for the segments
attribute.
The add()
method must add the given Segment
object to the segments
attribute.
The geocode()
method must return a List
of OnSegmentLocation
objects that contain the house number of interest. This List
need
not be ordered in any particular way. The returned List
may be empty but this
method must not return null
. (Hint: This method should use the interpolate()
method
in the Segment
class.)
The MapReader
Class
The readSegments()
method must read a collection of Segment
objects from a .seg
file. Any records in the .seg
file that are
incorrectly formatted (e.g., contain the wrong number of fields,
or contain non-numeric values for fields that must be numeric) must
be ignored. The Map
that is returned must use the ID of the
Segment
as they key. (Hint: The readLine()
method in the BufferedReader
class will return null
when the end-of-stream/end-of-file character has been
reached. The Scanner
class has hasNext()
and hasNextLine()
methods that
can be used to determine if the end-of-stream/end-of-file character
has been reached.)
The readStreets()
method must read a collection of Street
objects from a .str
file, using the collection of Segment
objects that it is passed. The Map
that is returned must use the
name of the Street
as the key.
The Geocoder
Class
The Constructor
The constructor will be passed the name (with no extension) of the
.seg
and .str
files to use (with no path information, meaning the file
can be found in the working directory). So, for example, for the JMU
area this method will be passed the String
"jmuarea"
(not
"jmuarea.seg"
or "jmuarea.str"
, and not "/cs159/eclipse/pa4/src/testing/jmuarea"
).
The constructor must use the MapReader
class to read the
.seg
and .str
files (from the working directory) and store the
information they contain in the Map
attribute named streets
.
Your implementation must not read either data file more than
once per execution. If it does, you will receive a grade of 0. In
other words, your implementation must read the file once (essentially
at the start of the execution), store all of the information needed in
the Map
named streets
, and then use that Map
to geocode the
addresses entered by the user. (Hint: There is no reason to keep the segments
after the constructor returns. They are only needed to read the streets.)
The fromAddress(String name, int number)
Method
The fromAddress()
method is passed an address (i.e., a street name and a
house/lot number) to geocode. The String
parameter will contain the
street name. The int
parameter will contain the house/lot number on
the street.
The fromAddress()
method must return a List
of OnSegmentLocation
objects that contains
all of the longitude/latitude pairs that correspond to the given
address (and nothing else).
If the address can’t be geocoded, the List
must contain no elements (but must be non-null
).
If the address can be geocoded, the calculated longitude and latitude must be a linear interpolation
of the longitudes and latitudes at the low and high end of the relevant segment. If the address can be geocoded
using more than one segment (i.e., there is more than one segment with the given street name that contains the
given house/lot number), the List
must be sorted in ascending order by segment ID.
(Hint: This method should use the geocode()
method in the Street
class.)
Testing
Nearby has provided information for two areas that you can use for
testing, each of which consists of two files. They are available for
download from the following URLs:
and
You must put these files in the project directory so that your Gecoder
will find them in the working directory when the code is executed. (See the
section on “Help with Data Files” below.)
Obviously, for all of your tests, you will need a way to calculate the
expected results (so that you can compare them with the actual
results). While you can use a calculator for this purpose, it’s
probably better to use a spreadsheet. Nearby has provided one that is
available for download at:
tiny.xls
It contains one row for each segment in the tiny
map. If you enter an address
in the appropriate row of the “Address” column, it will calculate the interpolation
parameter for that address, as well as the longitude and latitude for that address.
For example, entering the address 1970 in cell L5 will yield the same results
(in cell N5, O5 and P5) as in the example above.
Unit Testing
You should by now realize that you should test individual components before
testing the entire system. Hence, you should write JUnit tests for all of
the classes your write as you complete them (or as you complete indiviual
methods in them). However, you are not required to do so. In other words, you
will not be submitting your tests, you should write them because you know that
doing so is part of a good process.
Nearby has provided a few unit tests to help you get started. They are
available for download at:
These tests may need to be modified/corrected to be useful, and certainly need to be
expanded.
Note that it may not be possible to completely cover all of the
statements in the classes your write because it may not be possible to generate
all of the exceptions that might be thrown. Nonetheless, you should
try to cover as much of the code as possible.
Integration Testing
After you are confident that all of the methods in all of your classes pass your unit
tests, you should perform integration testing (in which you ensure that they
all work correctly together). You should do this by creating JUnit tests for the Geocoder
class
(which, directly or indirectly, uses all of the other classes). Note that there is no
main class included in this assignment. So, if you do not want to use JUnit for
integration testing, you will need to design and write a main class on your own.
Regardless, make sure you use an appropriate tolerance when comparing double
values.
Submission
You must submit (using Gradescope) a .zip
file named pa4.zip
that contains:
- Your implementation of all of the classes
packaged appropriately. The analytics
and geog
directories/folders must be at the top of the .zip file.
(See the first programming assignment if you have forgotten how to do this.)
Do not submit your JUnit tests and do not submit any data files.
Your grade will be reduced by 5 points for each submission after the 10th submission.
So, you should try to ensure that your code is correct before you submit it the first time.
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 (in Gradescope, this will be indicated in the
section on “Does your code compile?”) and all class names and method
signatures must comply with the specifications (in Gradescope, this
will be indicated in the section on “Do your class names, method
signatures, etc. 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 | All or Nothing; Success Required |
Correctness | 100 | Partial Credit Possible |
As discussed above, 5 points will be deducted for every submission after the 10th.
Gradescope will provide you with hints, but may not completely
identify the defects in your submission.
Manual Grading
After the due date, the Professor may manually review your code. At
this time, points may be deducted for duplicate code, inelegant code, inappropriate
variable names, bad comments, etc.
Recommended Process
At this point in the semester, you should be able to create a good process and use it.
You should probably think about which classes use which other classes and work from the bottom up. In other
words, start with the classes that stand alone (the bottom), then move up to the classes that use them,
and keep moving up until you get to the Geocoder
class.
Help
You might find the following discussions helpful.
Help with Data Files
You should download the data files into the downloads
directory/folder that you created for this course.
After you have downloaded the data files you must open a file explorer or finder, select all of the files, and drag them
into Eclipse. Specifically, you must drag them into the project (not the src
directory/folder or anything underneath it). When you do so, make sure you select “Copy files” not “Link to files”.
(It is possible to put the data files elsewhere, but then, when you submit your solution, your code
will not be able to find them on the Gradescope server. In other words, for your code to work both on your computer and on Gradescope,
the data files must be dragged into the Eclipse project.)
Then, in your code that needs to use these files, you must use only the file name and extension (i.e., do not include a path). For example,
you might construct a BufferedReader
named in
as follows:
BufferedReader in;
in = new BufferedReader(new FileReader("tiny.seg"));
All of the data files will be available on Gradescope for you to use in your tests (assuming you follow the instructions above).
Help with Testing
The jmuarea
files are about 3Mb in total, which means that it will
be very difficult for you to debug your code using them. Hence, you
should use the smaller tiny
files for initial testing and debugging.
These files contain four streets, two with one segment each and two
with multiple segments.
Even with the small files, you will need to be systematic in the tests
you use. Think carefully about the kinds of faults your implementation
might contain and the kinds of trigger conditions that might allow you
to detect them. Some important addresses to geocode are addresses at
the low and high ends of the segment which need not be interpolated (i.e.,
will have interpolation parameters of 0 and 1 respectively), and addresses
that are in the interior of the segment and must be interpolated (i.e.,
will have an interpolation parameter between 0 and 1).
For tiny.str
and tiny.seg
you should certainly start with the
following test inputs. Of course, you’ll first need to calculate the
expected answer for each of these inputs.
Addresses at Ends of Segments (i.e., No Interpolation Required)
- 748 Milk Spring Rd
- 798 Milk Spring Rd
- 674 Milk Spring Rd
- 746 Milk Spring Rd
- 1900 Creek Ct
- 1999 Creek Ct
- 1 Creek Loop
- 246 Creek Loop
- 387 Hill Dr
- 150 Hill Dr
Addresses in the Interior of Segments (i.e., Must Be Interpolated)
- 750 Milk Spring Rd
- 701 Milk Spring Rd
- 1935 Creek Ct
- 200 Creek Loop
- 155 Hill Dr
- 380 Hill Dr
No Such Street
- 747 Buttermilk Spring Rd
- 1 Creekside Ct
- 700 Spring Rd
- -1 Hilliard Dr
No Such Number
- 800 Milk Spring Rd
- 1750 Creek Ct
- -1 Hill Dr
Multiple “Hits” (i.e., Segments Containing the Address) Involving Addresses at the End and in the Interior
- 165 Hill Dr
- 303 Hill Dr
- 305 Hill Dr
- 359 Hill Dr
Multiple “Hits” Involving Addresses in the Interior
Questions to Think About
You don’t have to submit your answers to these questions, but you
should try to answer them before you start writing the code because they
will help you understand how to do so.
- Why is the
streets
attribute in the Geocoder
class a
Map<String,Street>
rather than some other kind of collection?
- Why is the
segments
attribute in the Street
class a List<Segment>
rather than some other kind of collection?
- Why does the
readSegments()
method in the MapReader
class return
a Map<String,Segment>
rather than some other kind of collection?
Can a defect in a test (e.g., an incorrect expected value, an inappropriate tolerance)
make you think code is correct when it isnt?
What should you do when your code fails an integration tests after passing your unit tests?
2 - PA3: FoneDrone
Programming Assignment 3
![FoneDrone](FoneDrone_Logo.png)
🛑 To Know Before You Start
There is no partial credit on this assignment. If your code does
not work correctly (or is not recursive), you will receive
a grade of 0.🛑 Using Piazza for PA3
Be very careful when using Piazza for PA3. As always, you must not include code in your posts.
In addition, you must now be careful when your posts involve discussions of the base case
and the refinement process. You may ask general questions about recursion, but your posts must
not include details about what you think the base case is or how your are refining the difficult cases.
If you have specific questions about the base case and the refinement process you should ask
your professor directly (either in person or using email).Learning Objectives
This homework/programming assignment is designed to help you learn
about recursion. It is also designed to help you think about what it
means for code to be correct, and why programmers shouldn’t be
satisfied with code that is “almost correct”.
Overview
Old people, like one of the professors teaching CS159 this semester,
have a tendency to lose their cell phones (and to have problems with
their beepers, fax machines, and land-line phones). While there are
currently tools that help people find lost phones, they seem to be
beyond the capabilities of old people. This has led to the
development of FoneDrone, an “intelligent” drone that can fly around
inside of a building and find cell phones.
The drone itself uses short range transmitters/receivers (e.g.,
bluetooth or near-field communications) to identify phones. Hence, the drone
must be directly over a phone to identify it.
The people that developed the FoneDrone hardware have little or no experience
developing software, so they have contracted with you to develop the
“intelligence” for FoneDrone.
Background
Rather than have you work with an actual drone, you have been provided
with a drone simulator and several simulated rooms. The simulation is
in the following .jar
file (that contains .class
files only, no .java
files):
and the simulated rooms (that are used by the simulation) are in the following “human-readable” text files:
You should download all of these files into the downloads directory/folder that you created
for this course.
The simulator consists of one enum, Direction
, and two classes,
Drone
and DroneController
. You must write the FoneDroneController
class. The capabilities of these components and the relationships between
them are illustrated in the following UML class diagram.
![Class Diagram](Design.png)
Specifications for the FoneDroneController
Class
In addition to the specifications in the UML class diagram,
your implementation of the FoneDroneController
class must conform
to the following specifications.
You may add private attributes and methods to the FoneDroneController
class as needed.
The explicit value constructor in the FoneDroneController
class
is passed the phone number to look for.
The drone need not search every location (i.e., it may stop searching
when it has found the phone it is looking for), but it may.
The drone starts (i.e., is “powered on”) at a particular location.
When it is done searching, it must return to that location.
When the drone first searches a particular location it should check
to see if the desired phone is at that location. This can be
accomplished by having the FoneDroneController
invoke the drone
object’s checkForPhone()
method (which returns the location as an
int[]
if the phone is at that location, or null
if the phone is not at that location). The
FoneDroneController
should keep track of the location of the phone
in its location
attribute (and return it when the
getLocationOfPhone()
method is invoked).
The getLocationOfPhone()
is a simple accessor. It must return the
location
attribute, it should not do the work of finding the phone.
The work of finding the phone should be done in the start()
method
or a private method that is invoked by the start()
method.
Testing
You may use the following main class to test your code:
package app;
import java.lang.reflect.InvocationTargetException;
import javax.swing.SwingUtilities;
import controller.*;
import simulator.*;
/**
* A driver for running FoneDroneController.
*/
public class FoneDrone {
/**
* The entry-point of the application.
*
* @param args The command line arguments (which are ignored)
* @throws InterruptedException If something goes wrong with the GUI
* @throws InvocationTargetException If something goes wrong with the GUI
*/
public static void main(String[] args) throws InterruptedException,
InvocationTargetException {
Drone drone;
FoneDroneController controller;
controller = new FoneDroneController("540-568-1671");
drone = new Drone(controller, "halls.txt");
controller.setDrone(drone);
SwingUtilities.invokeAndWait(drone);
}
}
It constructs a Drone
(for a particular room) and a
FoneDroneController
and makes them aware of each other.
When running, the drone simulator looks something like the following
(for the file named "halls.txt"
):
![Example](example.png)
The black areas indicate walls (or other places where the
drone can’t go), the gray areas indicate rooms (or other places
where the drone can go), the white areas indicate locations that
have already been searched, and the * indicates the drone.
Since you are viewing the simulator from above and the drone does not
change its orientation, Direction.FORWARD
is
always toward the top of the window, Direction.BACKWARD
is always toward the bottom
of the window, Direction.RIGHT
is always toward the right of the window,
and Direction.LEFT
is always toward the left of the window.
For testing and debugging purposes:
You can control the speed of the drone using the slider at the
bottom of the window. You can change the speed before the simulation
starts and while it is running. The smaller the delay (in
milliseconds) between moves, the faster the drone travels.
You can start the drone by clicking on [Drone] and pulling down
to [Start]. This causes the FoneDroneController
object’s start()
method to be invoked.
After the FoneDroneController
object’s start()
method returns,
its getLocationOfPhone()
method is invoked, and the result is
displayed in the console. (Note: This is where the Drone
thinks it
found the phone; the Drone
may not be correct.) Also, information about the drone’s
final location is displayed in the console.
You should have the Drone
search for Prof. Bernstein’s, Prof. Mayfield’s
and Prof. Stewart’s phones (they all have been known to leave them
behind) in all three rooms. Prof. Duan’s phone number has changed since the
simulator was created, so her current phone number shouldn’t be found.
(You can find all of their phone numbers in
the JMU Directory ).
You should also have the Drone
search
for your phone in all three rooms (which it shouldn’t find).
Submission
You must submit (using Gradescope) a .zip
file named pa3.zip
that contains:
FoneDroneController.java
packaged appropriately. The controller
directory/folder
must be at the top of the .zip
file. (See the first programming assignment if you
have forgotten how to do this.)
Do not submit any other classes (including any unit tests).
Your grade will be reduced by 5 points for each submission after the 10th submission.
So, you should try to ensure that your code is correct before you submit it the first time.
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 (in Gradescope, this will be indicated in the
section on “Does your code compile?”) and all class names and method
signatures must comply with the specifications (in Gradescope, this
will be indicated in the section on “Do your class names, method
signatures, etc. 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 | Success Required |
Correctness | 100 points | All or Nothing |
In other words, your code is either correct (i.e., the Drone
finds
all of the lost phones and doesn’t find any phones
that aren’t lost) or it isn’t.
As discussed above, your grade will be reduced by 5 points for each submission
after the 10th submission.
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, etc. Points may also be deducted for using
randomization to choose directions.
If your solution is not recursive (in a meaningful way), you will receive
a grade of 0.
Help
You should carefully consider all of the following topics before starting.
Help with Data Files
As discussed above, you should download the data files into the downloads directory/folder that you created for this course.
After you have downloaded the data files you must open a file explorer or finder, select all of the files, and drag them
into the Eclipse graphical user interface. Specifically, you must drag them into the project (not the src directory/folder or anything underneath it). When you do so, make sure you select “Copy files” not “Link to files”.
Then, when you construct a Drone
, you must use only the file name (i.e., do not include a path) as in the example above.
You do not need to read these files, they are read by the simulation. You just need to tell the Drone
which file to use.
Help Using .jar Files
As discussed above, you should download the .jar
file into the downloads directory/folder that you created for this course.
Help incoroprating .jar
files into an Eclipse project is available on the
CS Wiki .
Help Allocating Time
Almost all of your time on this assignment should be spent thinking;
very little time should be spent typing/compiling/etc. Indeed, the
recursive method in the FoneDroneController
class may contain as few
as 20 lines of code.
Though it may have worked for you in the past, it is very unlikely
that you will discover an algorithm that solves this problem using
trial and error (i.e., trying something, compiling, testing, making a
change, and repeating). You will need to think carefully about how to
solve problems recursively in general, and how recursive thinking can
be used to solve this problem in particular.
Help with Class Design
The start()
method in your FoneDroneController
class should not
contain all of the control logic. It should call other methods (that
you will need to write), at least one of which must be recursive.
Help Getting Started
The people who developed FoneDrone originally contracted with students at
another university in Virginia to develop the “intelligence”. They didn’t
understand recursion and so were only able to develop a FoneDroneController
that could search forward (in a stright line) from the Drone
object’s
original position. (We won’t mention which university so as not to embarass them.)
The people who developed FoneDrone have provided you with this FoneDroneController
in case it will help you get started (and help you understand what not to do).
(Remember to edit the comments after you make changes.)
package controller;
import simulator.*;
/**
* INCORRECT control logic for the PhoneDrone.
*
* @author A team of "hoakey" programmers at an un-named university in VA.
* @version 1.0
*/
public class FoneDroneController extends DroneController {
private int[] location;
/**
* Explicit Value Constructor.
*
* @param number The phone number to look for
*/
public FoneDroneController(String number) {
super(number);
this.location = null;
}
/**
* Get the location of the phone (or null if it hasn't been found).
*
* @return The location of the phone (or null)
*/
public int[] getLocationOfPhone() {
return location;
}
/**
* Start the FoneDrone.
*/
@Override
public void start() {
search();
}
/**
* Search for the phone.
*
* This method should be recursive but we couldn't figure
* out how to do that. So, we did this instead. It's
* close to correct because it does find the phone
* sometimes. As a result, we think we should get at
* least partial payment. In fact, because of the amount of
* effort we devoted to it, we really think that we
* deserve full payment.
*/
private void search() {
while (drone.canMove(Direction.FORWARD) && (location == null)) {
drone.move(Direction.FORWARD);
location = drone.checkForPhone(number);
}
while (drone.canMove(Direction.BACKWARD)) {
drone.move(Direction.BACKWARD);
}
}
}
Help Writing a Recursive Algorithm
Remember that there are two parts of a recursive algorithm:
The part of the algorithm that handles the “easy” case.
The part of the algorithm that refines “difficult” cases.
The first thing to think about is how to define the “easy” case
for this problem. To
do so, suppose the drone is at a particular location and is told to
search. When, in terms of the data (i.e., its location in the room,
ignoring the location of the phone), is its job really easy?
After you have identified the “easy” case, think about how you can
move the “difficult” cases (in terms of the data) to the “easy” case.
In other words, how can you make progress (i.e., make the current
situation “easier”) by calling methods in the Drone
class like move()
,
canMove()
, and hasBeen()
.
If you have trouble answering either of these questions after thinking about them
for a few hours, ask your Professor for help (but make sure that you have evidence
that you have thought seriously about answering these questions before you
ask for help).
Thinking About Other Recursive Algorithms
Thinking about other recursive algorithms may help you write this one.
Finding the Factorial of n
The base case is when n
is 1 (or 0) since all you have to do in this
case is return the value 1. Refinement involves moving from the hard
case toward the easy case (adjusting the answer accordingly). So,
refinement involves reducing the value by 1.
Finding the Size of a Directory/Folder
A directory/folder can contain files and other directories/folders.
The base case is when we you are examining a file since all you have
to do in this case is return its size. Refinement involves
moving from the hard case toward the easy case (adjusting the answer
accordingly). So, refinement involves entering each directory/folder.
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.
If your FoneDroneController
works correctly and your phone
is not found at a particular location, do you need to search at that location
again? (You can assume that your phone does not move.)
If your FoneDroneController
works “sometimes” and your phone is
not found at a particular location, have you learned anything?
If the software in the air traffic control system works correctly
and assures a pilot that there are no planes on the runway that they
have been directed to, can the pilot land confidently?
If the software in the air traffic control system works “sometimes”,
can a pilot ever land confidently?
Would you fly on an airplane if the software in the air traffic
control system and the software in the plane’s avionics system work
“sometimes”?
Would you complain if Canvas contained defects that led it to read
and write grade files incorrectly every time it did so? Suppose it only
read and wrote grade files incorrectly “sometimes”?
3 - PA2: Big Box Bargains
Programming Assignment 2
![Big Box Bargains](BBB_Logo.png)
🛑 To Know Before You Start
Though the classes you must write are not terribly large, and the
input/output is similar to what was required to complete the previous
assignment, this assignment will be fairly time consuming. This is both
because it deals with the most difficult concepts that we have considered
this semester, and because you will need to spend a lot of time
writing tests.Learning Objectives
This programming assignment is designed to help you learn
several things. First, it will help you learn about interfaces and
abstract classes, and improve your understanding of
specialization. Second it will help you learn about polymorphism and
dynamic binding. Third, it will help you learn about static binding.
Finally, it will help reinforce your understanding of I/O, packages,
and unit testing.
Overview
Big Box Bargains (BBB), a new “big box” store that hopes to compete with
stores like Costco and Sam’s Club, has contracted with you to
write some of the software they need for their checkout system.
The Components to be Written
You must write the Account
and VIPAccount
classes that encapsulate
membership information that is specific to BBB, as well as some
general purpose components for performing input/output operations on
comma-separated-value (CSV) files. The relationship between these components
is illustrated in the following UML class diagram.
![Class Diagram](Design.png)
Note that methods in a class that are “concrete” implementations of abstract methods
(that are listed either in an interface or an abstract class) are not explicitly listed in
the class (e.g., the toCSV()
method in the Account
class). Method in a derived class
that override a method in a base class are explicitly listed in the derived
class (e.g., the toCSV()
method in the VIPAccount
class).
🛑 Italics
On some displays, it may be difficult to distinguish the italic font from the upright font
in this class diagram. So that there is no confusion, the following are italicized:
the fromCSV()
and toCSV()
methods in the CSVRepresentable
interface,
the name of the FileProcessor
class, and the decrypt()
and encrypt()
methods in the FileProcessor
class.The CSVRepresentable
Interface
The CSVRepresentable
interface describes the capabilities of objects
that can be represented in simplified CSV format (i.e., all of the fields
are delimited by commas, the fields are assumed to not contain commas,
and there are no column headers). The toCSV()
method says that
such an object must be able to create a String
representation of
its attributes in simplified CSV format. The fromCSV()
method, on the other hand, says that such an
object must be able to initialize its attributes from a String
representation in simplified CSV format.
Obviously, the fromCSV()
method must tokenize the String
that it
is passed (that has a comma as the delimiter between tokens). In
principle, this can be done in many ways, but you must choose between
two. Specifically, you must use either a Scanner
or a StringTokenizer
(both of which are in the java.util
package) for this purpose. The
fromCSV()
method returns the object used for tokenizing the String
so that it may be used to tokenize any remaining tokens in the
String
. In other words, the String
representation may contain more
tokens than the fromCSV()
method uses, so it must return the object used
for tokenzing in case these tokens need to be used elsewhere.
The FileIdentifier
Class
The FileIdentifier
class encapsulates the “classic” two-part approach
to identifying files. In this “classic” approach, a file identifier
consists of a name (the portion to the left of the dot) and an
extension or type (the portion to the right of the dot). So, for
example, one might identify the file named Student.java
using a
FileIdentifier
that has a name
attribute of "Student"
and an
extension
attribute of "java"
.
This class implements/realizes the CSVRepresentable
interface because
it is common to include FileIdentifier
objects in CSV files.
The toString()
method must return the name
and extension
formatted using the
format String
"%s.%s"
.
The FileProcessor
Class
The FileProcessor
class is an abstract encapsulation of an object
that can read from and write to the file system in a line-oriented
way. The first line of any file it reads or writes must contain the
number of lines in the file.
This class supports encryption and decryption, but those capabilities
must be provided by derived classes. In addition to the
specifications contained in the UML class diagram, this class must
conform to the following specifications.
The readLines()
method must first read the number of lines in the
file. It must then read the subsequent lines, decrypt them, and return
a String[]
array that contains the decrypted lines.
The writeLines()
method must first write the length of the parameter
named lines
(on a line of its own). It must then encrypt the elements and write
them to the file (one element per line).
The CSVFileProcessor
Class
The CSVFileProcessor
class is a concrete specialization of the
FileProcessor
class. In addition to the specifications contained in
the UML class diagram, this class must conform to the following
specifications.
The decrypt()
and encrypt()
methods must return the parameters
they are passed unchanged. In other words, CSV files must not be
encrypted.
The write()
method must create a CSV representation of the objects it is
passed and write all of them to the appropriate file (that is passed to the constructor), one per line.
The read()
method must read all of the records from the appropriate file
(that is passed to the constructor) and
initialize the attributes of the objects that it is passed. It must
assume that it is passed an array of objects that has the same number
of elements as there are records (one per line) in the file. (Note: This method does not
return the elements it reads. Instead, it sets the attributes of each
CSVRepresentable
object it is passed, using that CSVReprentable
object’s fromCSV()
method.)
The write()
and read()
methods must not duplicate any code in
the FileProcessor
class or in the class of the CSVRepresentable
objects that it is passed.
The Account
Class
The Account
class is an encapsulation of a membership account at BBB.
In addition to the specifications contained in the UML class diagram,
this class must conform to the following specifications.
The purchases
attribute must be used to keep track of the total
purchases made by the member (measured in dollars),
and the creditsUsed
attribute must be used to keep track of the
total credits used by the member (measured in dollars).
The default constructor must initialize both attributes to 0.00.
The availableCredit()
method must return the available credit, which is
based on a percentage (obtained from getRewardPercentage()
) of
the purchases
attribute minus the credits used to date. So, for example,
if purchases
is 2000.00, getRewardsPercentage()
returns 0.01, and creditsUsed
is 5.00, then this method must return 15.00.
Note that the value returned must never be less than 0.00.
So, for example, if purchases
is 2000.00, getRewardsPercentage()
returns 0.01, and creditsUsed
is 25.00, then this method must return 0.00.
An Account
holder can use the express line when the total
purchases made by the member is greater than 1000.00.
The getRewardPercentage()
method must return 0.01 (i.e., 1 percent)
for Account
holders.
The increaseCreditsUsed()
method must increase the creditsUsed
attribute by the given amount. It must never decrease the
creditsUsed
attribute (i.e., it must do nothing when the parameter
is negative).
The increasePurchases()
method must increase the purchases
attribute by the given amount. It must never decrease the
purchases
attribute (i.e., it must do nothing when the parameter
is negative).
The purchase()
method must process a purchase of the given
amount
(when it is greater than 0.00), using as many credits as are
available when the applyCredits
parameter is true
(in which case
it must invoke the increaseCreditsUsed()
method, passing it the
appropriate value). It must invoke the increasePurchases()
method
passing it the amount due (i.e., the amount of the purchase less any
credits that are used), and must return the amount due. For example,
if passed 100.00
and true
when the Account
holder has 20.00
credits available, it must pass 20.00
to increaseCreditsUsed()
,
pass 80.00
to increasePurchases()
and must return 80.00
. The
amount due must never be less than 0.00, but can be 0.00 (when enough
credits are available). The credits used must never be
greater than the purchase.
The toCSV()
method must return a String
representation of the
purchases and credits used, formatted using a format String
of
"%.2f,%.2f"
.
The fromCSV()
method must set the attributes of the
owning object. It may assume that the String
it is passed was created by
the toCSV()
method and must not do any error checking.
The toString()
method must use return a String
representation
of the purchases, credits used, and available credit, formatted using a
format String
of
"Purchases: %.2f\nCredits Used: %.2f\nCredits available: %.2f"
.
The VIPAccount
Class
The VIPAccount
class is an encapsulation of an exclusive membership
account at BBB. (“VIP” is an acronym for “very important person”.)
In addition to the specifications contained in the
UML class diagram, this class must conform to the following
specifications.
The visits
attribute must keep track of the number of visits to
BBB in which the member made a purchase.
The default constructor must initialize all attributes to 0.00.
A VIPAccount
holder can use the express line whenever an
Account
holder can. A VIPAccount
holder can also use the
express line when they have 10 or more visits.
A VIPAccount
holder has the same starting reward percentage
as an Account
holder, however, it can increase. Specifically,
for every ten visits, a VIPAccount
holder’s reward percentage
increases by 0.01 up to a maximum of 0.15 (i.e., it can bever be larger
than 15 percent). For example, a VIPAccount
holder who has made 26 visits will
have a reward percentage of 0.03 (i.e., 0.01 + 0.02) and a VIPAccount
holder
who has made 371 visits will have a reward percentage of 0.15.
Each time the increasePurchases()
method is invoked, it must increase
the number of visits by 1. Of course, it must also increase the
purchases
attribute in the base class.
The toCSV()
must return a String
representation of the result
of invoking the toCSV()
method in the base class followed by the number
of visits, formatted using a format String
of "%s,%d"
.
The fromCSV()
method must set all of the attributes of the
owning object. It may assume that the String
it is passed was created by
the toCSV()
method and must not do any error checking.
The toString()
must return a String
representation of the result
of invoking the toString()
method in the base class followed by the number
of visits, formatted using a format String
of
"%s\nVisits: %d"
.
No method in the VIPAccount
class may duplicate code in the
Account
class.
Unit Testing
You must write JUnit tests for all of your classes. Your JUnit test
suite must cover all statements and all branches (as measured by
EclEmma) in all of the classes you write. Your tests must be in a
package named testing and each test class must include the word “Test”
in its name.
As in the past, your unit tests of the input/output methods must make use of round-trip
testing. So, you must create a JUnit test that first executes
the appropriate write method and then executes the appropriate read method.
Note that, to ensure that the write happens before the read, the two must be
in the same JUnit test. You can (and should) have multiple tests of this kind,
but each must be independent of the others.
Submission
You must submit (using Gradescope) a .zip
file named pa2.zip
that contains:
CSVRepresentable.java
, FileIdentifier.java
,
FileProcessor.java
, CSVFileProcessor.java
, Account.java
, and
VIPAccount.java
; and
Your JUnit tests
packaged appropriately. The io
, membership
, and testing
directories/folders
must be at the top of the .zip
file. (See the previous programming assignment if you
have forgotten how to do this.) Do not submit any .bbb
files.
Your grade will be reduced by 5 points for each submission after the 10th submission.
So, you should try to ensure that your code is correct before you submit it the
first time. In other words, you should not use Gradescope to check your style, to test your
code, or to ensure that your tests cover your code - you should do all of that on your
computer before you make any submissions to Gradescope.
Note that your submission will not be graded if
it does not comply with the specifications. So, if you do not complete
a class your submission should include a stubbed-out version of
it. This will allow you to potentially get credit for the methods/classes that
you do complete.
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 (in Gradescope, this will be indicated in the
section on “Does your code compile?”) and all class names and method
signatures must comply with the specifications (in Gradescope, this
will be indicated in the section on “Do your class names, method
signatures, etc. 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 | Success Required |
Passing Your Tests | 10 | All or Nothing; Success Required |
Coverage of Your Tests | 20 | Partial Credit Possible |
Correctness | 70 | Partial Credit Possible |
As discussed above, your grade will be reduced by 5 points for each submission after the
10th submission. Gradescope will provide you with hints, but may not completely identify the defects in your submission.
Manual Grading
After the due date, the Professor may manually review your code. At
this time, points may be deducted for code that does not conform
to the specifications, inelegant code, duplicate code,
inappropriate variable names, bad comments, etc.
Recommended Process
At this point in the semester, you should be able to create a good process
and use it.
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. Note that you do not need the source code for the
CashRegister
class to answer them.
Suppose you have a CashRegister
class in which a canUseExpressLine()
message is
sent to an Object
named member
, which is declared to be an
Account
but can actually be either an Account
or a
VIPAccount
. Both classes have such a method. What code is invoked
and why?
Suppose you have a CashRegister
class in which the availableCredit()
message is
sent to an Object
named member
, which is declared to be an
Account
but can actually be either an Account
or a
VIPAccount
. The only availableCredit()
method is in Account.java
so, obviously, that code will be invoked. However, the
getRewardPercentage()
method is then invoked, and this method is
implemented in both classes. What code will be invoked and why?
Suppose you have a CashRegister
class in which the purchase()
message is
sent to an Object
named member
, which is declared to be an
Account
but can actually be either an Account
or a
VIPAccount
. The only purchase()
method is in Account.java
so, obviously, that code will be invoked. However, the
increasePurchases()
method is then invoked, and this method is
implemented in both classes. What code will be invoked and why?
Suppose you have a CashRegister
class that has an overloaded addToLog()
method,
one which is passed an Account
and of which is passed a VIPAccount
.
Suppose further that this method is passed an Object
named member
which is declared to be an
Account
but can actually be an Account
or a VIPAccount
, both of
which implement the CSVRepresentable
interface. Which overloaded
version of addToLog()
will be invoked and why?
Why is the FileProcessor
class declared to be abstract?
Will the following fragment compile? Why or why not?
FileProcessor fp;
fp = new CSVFileProcessor("temp.csv");
In the previous programming assignment, the io
package was a subpackage of
the product
package. In this assignment, the io
package is not
inside the membership
package. Why not?
4 - PA1: Teethbrushes
Programming Assignment 1
![Teethbrushes](Teethbrushes_Logo.png)
🛑 To Know Before You Start
Since this is a programming assignment (not a homework assignment) it
is longer and will take more time than any of the assignments you have
completed so far.Learning Objectives
This programming assignment is designed to help you learn
several things. First, it will help you learn about specialization
(i.e., derived classes), inheritance, and overriden methods. Second,
it will help you learn about reading from and writing to the file
system (i.e., file input and output). Finally, it will help you become
familiar with packages.
Overview
You may have heard, or you may have observed first-hand, that computer
science students sometimes have personal hygiene problems. (Not you of
course, but perhaps some of your fellow students?) A recent survey
identified part of the problem. Many computer science students only
brush one tooth (rather than all of them) because they were told to use
a toothbrush and the word tooth is singular. An enterprising
company has decided to take advantage of this confusion by selling
teethbrushes instead.
At the moment, they are starting to build relationships. For example,
they have contracted with “Dr. Teeth and the Electric Mayhem” to write
their advertising jingles, and they have contracted with you to
implement some of the classes needed for their inventory system.
Background
A physical Teethbrush
consists of a head (and associated handle
or stem) while a physical ElectricTeethbrush
consists of a head
and a body. The bristles in the head can be flat or polished
(i.e., rounded). The body of an ElectricTeethbrush
can be
rechargeable or not, and can provide ultrasonic cleaning (via the
head) or not.
The cost of a Teethbrush
is the sum of the BASE_HEAD_COST
($2.00),
the hardness cost, and the POLISHED_UPCHARGE
($0.75) if the bristles
are polished. The hardness cost is the product of the hardness
(which is in the closed interval [0, 5]) of the bristles and the
HARDNESS_UPCHARGE
($0.10). So, the harder the bristles, the higher the
cost. For example, a Teethbrush
that has polished bristles with a
hardness of 5 costs $2.00 + $0.75 + $0.50 = $3.25
The head of an ElectricTeethbrush
costs the same as the head of a
Teethbrush
. However, an ElectricTeethbrush
comes with multiple
heads, whereas a Teethbrush
has only one. Hence, the cost of an
ElectricTeethbrush
is the product of the NUMBER_OF_HEADS
(5) and
the cost of a Teethbrush
, plus the BASE_BODY_COST
($5.00), plus
the RECHARGEABLE_UPCHARGE
($20.00) and the ULTRASONIC_UPCHARGE
($10.00) as appropriate. For example, a basic ElectricTeethbrush
that has polished bristles with a hardness of 0 has an initial cost of
$13.75 + $5.00 = $18.75, and a rechargeable version would cost $18.75 +
$20.00 = $38.75.
The shipping cost of an order is the product of the unit shipping
cost and the order size, plus a STOCKING_COST
($25.00) if the size of
the order is greater than the current inventory level. The unit
shipping cost is either the UNIT_SHIPPING_COST_OVERSEAS
($0.30) or
the UNIT_SHIPPING_COST_LOCAL
($0.10), reduced by the
LARGE_ORDER_DISCOUNT
(30.0%) if the order is greater than
LARGE_ORDER
(1000). For example, suppose someone wants to ship an
order of 2000 Teethbrush
overseas when there are 100 in
inventory. The unit shipping cost will be $0.21 (the $0.30 minus the
large order discount of $0.09), meaning the total shipping cost will be $445.00
(i.e., $0.21 x 2000 = $420.00 plus the $25.00 stocking cost).
The Classes to be Written
You must write the Teethbrush
, ElectricTeethbrush
, and
ProductFileProcessor
classes that are summarized in the following
UML class diagram.
![Class Diagram](Design.png)
Note that attributes and methods that are inherited are not made
explicit in this diagram. If a method appears in both a base class and
a derived class it means that the method in the derived class
overrides the method in the base class.
The Teethbrush
Class
As discussed above and as shown in the UML class diagram,
a Teethbrush
object is characterized by the hardness of the
bristles (on a scale of 0-5), and whether the bristles are polished or
not (i.e., whether the part of the bristle that comes in contacts with
the teeth is flat or rounded). A Teethbrush
object also has an
associated inventory level.
In addition to the specifications contained in the UML class diagram
and the background discussion above, this class must conform to the
following specifications.
- Any time someone attempts to set the
hardness
attribute to a
value less than 0 it must be set to 0. - Any time someone attempts
to set the
hardness
attribute to a value greater than 5 it must be
set to 5. - The
changeInventory()
method can be used to increase (with a positive parameter) or
decrease (with a negative parameter) the inventory
attribute. It must not change the
inventory
attribute (and must return false
) if the change would make the
inventory
attribute negative. Otherwise, it must make the change and return
true
.
The ElectricTeethbrush
Class
As discussed above and as shown in the UML class diagram, in addition
to the attributes of a Teethbrush
, an ElectricTeethbrush
can be
rechargeable or not, and can be ultrasonic or not.
In addition to the specifications contained in the UML class diagram
and background discussion above, this class must conform to the
following specifications.
- The
cost()
method in the ElectricTeethbrush
class must not
duplicate any code in the method that it overrides.
The ProductFileProcessor
Class
The ProductFileProcessor
is a utility class that can be used to
read/write Teethbrush
and ElectricTeethbrush
information from/to
the file system in (a simplified) comma-separated-valued (CSV) format
in which fields are delimited by a comma and there is a newline
character at the end of a record. For example, the CSV file for
an electric toothbrush might be as follows:
0,3,true,false,true
In addition to the specifications contained in the UML class diagram,
this class must conform to the following specifications.
- The file name to use when reading and writing must be the model
name followed by
".bru"
. So, for example, the information about
model "E1003"
must be in a file named "E1003.bru"
(in the working
directory/folder). - All objects used for reading and writing must be closed when they
are no longer needed.
- The
writeTeethbrush()
method must write the inventory
,
hardness
, and polished
attributes using "%d,%d,%b\n"
as the format
String
- The
writeElectricTeethbrush()
method must write the inventory
,
hardness
, polished
, rechargeable
and ultrasonic
attributes
using "%d,%d,%b,%b,%b\n"
as the format String
- The
readTeethbrush()
method must read a file written by the
writeTeethBrush()
method, construct and initialize a Teethbrush
object, and return it. - The
readElectricTeethbrush()
method must read a file written by
the writeElectricTeethBrush()
method, construct and initialize an
ElectricTeethbrush
object, and return it.
Unit Testing
You must write JUnit tests for the Teethbrush
, ElectricTeethbrush
, and
ProductFileProcessor
classes. Your JUnit test suite must cover all
statements and all branches (as measured by EclEmma) in all of the
classes you write. Your tests must be in a package named testing and
each test class must include the word “Test” in its name.
Submission
You must submit (using Gradescope) a .zip
file named pa1.zip
that contains:
Your implementation of the Teethbrush
, ElectricTeethbrush
, and
ProductFileProcessor
classes.
Your JUnit tests.
packaged appropriately.
Do not submit any .bru
files. To test the read methods in your
ProductFileProcessor
class you must create a JUnit test that first executes
the appropriate write method and then executes the appropriate read method.
(This is sometimes called “round trip” testing of input/output.) Note that,
to ensure that the write happens before the read, the two must be in the same
JUnit test. You can (and should) have multiple tests of this kind, but each
must be independent of the others.
There is no limit on the number of submissions and no penalty for
excessive submissions. Note that your submission will not be graded if
it does not comply with the specifications. So, if you do not complete
a class your submission should include a stubbed-out version of
it. This will allow you to potentially get credit for the classes/methods that
you do complete.
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 (in Gradescope, this will be indicated in the
section on “Does your code compile?”) and all class names and method
signatures must comply with the specifications (in Gradescope, this
will be indicated in the section on “Do your class names, method
signatures, etc. 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 | Success Required |
Passing Your Tests | 10 | All or Nothing; Success Required |
Coverage of Your Tests | 20 | Partial Credit Possible |
Correctness | 70 | Partial Credit Possible |
Note that some of the criteria are described as “Success
Required”. This means that Gradescope will not assess subsequent
criteria unless this criterion is satisfied (and you will receive a
grade of 0 for the criteria that aren’t assessed). So, for this
assignment, if your code does not conform to the style guide then
nothing else will be assessed (and you will receive a grade of 0 for
the assignment). Similarly, if your code does not pass your tests, nothing
else will be assessed and you will receive a grade of at most 10.
Gradescope will provide you with hints, but may not completely
identify the defects in your submission.
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, 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 described here.
Get Started
Read and understand the entire assignment.
In Eclipse, create a project for this assignment named
pa1
. (Remember, do not create a module, and create separate folders
for source and class files).
In Eclipse, create three packages (in the project) named product
, product.io
, and
testing
. Do not have Eclipse create package-info.java
for any of
these packages.
Activate Checkstyle for this assignment.
Create Test Cases for Teethbrush
and ElectricTeethbrush
By hand (i.e., using pencil and paper), create several black-box tests cases (i.e.,
inputs and expected outputs) for the cost()
method in the
Teethbrush
class. Some of these tests should involve random inputs and some
should involve rules-of-thumb.
By hand, create tests several black-box cases for the shippingCost()
method in the Teethbrush
class. Some of these tests should involve random inputs and some
should involve rules-of-thumb.
By hand, create sevearl black-box tests cases for the cost()
method in the
ElectricTeethbrush
class. Some of these tests should involve random inputs and some
should involve rules-of-thumb.
Writing these black-box test cases before you start writing the code is called test driven development (TDD).
Using TDD will help you
understand the algorithms that you need to write.
Stub-Out the Classes
- Stub-out the
Teethbrush
class. - Stub-out the
ElectricTeethbrush
class. - Stub-out the
ProductFileProcessor
class. - Add the “javadoc” comments to the three classes class.
- Check the style of the three classes and make any necessary corrections.
Implement and Test the Classes
Implement the constructor, setters, and simple getters in the
Teethbrush
class.
Test the constructor, setters, and simple getters in the
Teethbrush
class using the black-box tests (and debug if necessary).
If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).
Implement the constructor and simple getters in the
ElectricTeethbrush
class.
Test the constructor and simple getters in the ElectricTeethbrush
class using the black-box tests (and debug if necessary).
If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).
Implement the shippingCost()
method in the Teethbrush
class.
Test the shippingCost()
method in the Teethbrush
class using the black-box tests (and
debug if necessary).
If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).
Implement the cost()
method in the Teethbrush
class.
Test the cost()
method in the Teethbrush
class using the black-box tests (and debug if
necessary).
If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).
Implement the cost()
method in the ElectricTeethbrush
class.
Test the cost()
method in the ElectricTeethbrush
class using the black-box tests (and
debug if necessary).
If necessary, add white-box tests to get 100% statement coverage and 100% branch coverage (and debug if necessary).
Use the same kind of process to develop, test, and debug the ProductFileProcessor
class.
Help
Help is available on the following topics.
Help Creating Packages
Help with creating packages in Eclipse is available on the Department’s
Wiki at:
Help Creating Files (Including Files in a Package)
Help with creating files in Eclipse (including files in a package) is available on the Department’s
Wiki at:
Help with JUnit
You should be fairly proficient with JUnit at this point (especially since we will be using JUnit for the rest of the semester).
However, if you are still having trouble, help is available at:
Help with Coverage
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.
For more information, see:
Help Creating the .zip
File
The directories/folders containing the packages must appear at the top
of the .zip
file. So, you must not compress the directory/folder
containing these directories/folders, you must compress the
directories/folders containing the packages. In other words, if you
have a directory named src
that contains the directories product
and testing
, you must not compress src
, you must compress
product
and testing
. (Note that the io
directory/folder will be
inside of the product
directory/folder.)
Help with creating .zip
files is available on the CS Wiki. In
particular, see:
Note that your .zip
file must only contain .java
files, not
.class
files. This will be much easier to accomplish if, when you
create your project, you instruct Eclipse to use separate
directories/folders for source and class files.
Help Using a Scanner
There are two delimiters used in CSV files, the comma that separates fields
and the newline character that separates records (or lines). To instruct
a Scanner
to use a particular delimiter or delimiters you must invoke its useDelimiter()
method. To instruct it to use just commas as delimiters you must pass it ","
,
to instruct it to use just newlines as delimiters you must pass it "\n"
, and
to instruct it to use both you must pass it "[,\n]"
(which is a regular expression
that matches both).
Help Using a BufferedReader
There are two delimiters used in CSV files, the comma that separates fields
and the newline character that separates records (or lines). If you choose
to use a BufferedReader
you will read an entire record (including the newline
character) using the readLine()
method. You can then tokenize it either using the
split()
method in the String
class (passing it ","
as the delimiter) or using
a StringTokenizer
object (again using ","
as the delimiter).
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.
Would the design of the Teethbrush
class be improved by using a
0-1 indicator for the polished
attribute (as discussed in
Chapter 6 of Patterns for Beginning Programmers )
rather than using a boolean
value? What would change in the constructor, cost()
method, and
isPolished()
method?
What is gained by using specialization in this assignment? In other words,
what is gained from having the ElectricTeethbrush
class extend the
Teethbrush
class?
Is there code duplication in the “read” methods of the
ProductFileProcessor
class? Why is this troubling?
Is there code duplication in the “write” methods of the
ProductFileProcessor
class? Why is this troubling?
Given the number of classes in this assignment, are the packages
that helpful? When is the use of packages likely to be more helpful?