Description
Overview: This project is Part 3 of three that that will involve calculating the estimated monthly cost
of owning a bunny where the amount is based on the type of bunny, its weight, and various additional
costs. In Part 1, you developed Java classes that represent categories of bunny: pet bunny, house
bunny (a subclass of pet bunny), jumping bunny, and show bunny. In Part 2, you implemented three
additional classes: (1) CostComparator that implements the Comparator interface, (2) BunnyList that
represents a list of bunnies and includes several specialized methods, and (3) BunniesPart2 which
Project: Bunnies – Part 3 Page 2 of 9
Page 2 of 9
contains the main method for the program. Note that the main method in BunniesPart2 should create
a BunnyList object, read the data file using the readBunnyFile method. BunniesPart2 then prints the
summary, the bunnies listed by name and the bunnies listed by estimated monthly cost, and the list of
excluded records. In Part 3 (this project), you are to add exception handing and invalid input
reporting. You will need to do the following: (1) create a new class named NegativeValueException
which extends the Exception class, (2) add try-catch statements to catch FileNotFoundException in
the main method of the BunniesPart3 class, and (3) modify the readBunnyFile in the BunnyList class
to catch/handle NegativeValueException, NumberFormatException, and NoSuchElementException
in the event that these type exceptions are thrown while reading the input file.
Note that the main method in BunniesPart3 should create a BunnyList object and then invoke the
readBunnyFile method on the BunnyList object to read data from a file and add bunnies to the
bunnyList array in the BunnyList object. You can use BunniesPart3 in conjunction with interactions
by running the program in the canvas (or debugger with a breakpoint) and single stepping until the
variables of interest are created. You can then enter interactions in the usual way. You should create
a jGRASP project upfront and then add the source and test files as they are created. All of your files
should be in a single folder.
• Bunny, PetBunny, HouseBunny, JumpingBunny, and ShowBunny
Requirements and Design: The constructors for these classes must be modified to check
numeric parameters specific to their respective classes for negative values and to throw a
NegativeValueException as appropriate. For example, if -8.5 is passed into the Bunny
constructor (via super) as the bunny’s weight, the constructor should throw a
NegativeValueException. A second example, if -0.5 is passed into the HouseBunny constructor
as wearAndTear, the constructor should throw a NegativeValueException. Since these
constructors for Bunny, PetBunny, HouseBunny, JumpingBunny, and ShowBunny are not
catching this exception, they must include NegativeValueException in the throws clauses of their
respective headers. Since the Bunny constructor gets called when any constructors in Bunny
subclasses are called, subclass constructors must also have throws negativeValueException in the
constructor headers.
Testing: Since the constructors in Bunny, PetBunny, HouseBunny, JumpingBunny, and
ShowBunny may throw a NegativeValueException, any method that calls one of these
constructors must either catch the exception or it must throw the exception (i.e., include
NegativeValueException in a throws clause for your test method headers).
• NegativeValueException.java
Requirements and Design: NegativeValueException is a user defined exception created by
extending the Exception class with an empty body. The constructor for NegativeValueException
should be public and parameterless, and it should invoke the super constructor with the String
message “Numeric values must be nonnegative”. The inherited toString() value of a
NegativeValueException will be the name of the exception and the message. This exception is to
Project: Bunnies – Part 3 Page 3 of 9
Page 3 of 9
be caught in the readBunnyFile method in the BunnyList class when a line of input data contains
a negative value for one of the numeric input values: weight, wearAndTear, trainingCost,
groomingCost. The NegativeValueException is to be thrown in the “bunny” constructor that is
responsible for setting the field in question. The following shows how the constructor would be
called: new NegativeValueException() For a similar constructor, see
InvalidLengthException.java in Examples/Polygons from this week’s lecture notes on
Exceptions.
Testing: Here is an example of a test method that checks to make sure a negative value for value
in the constructor for Bunny throws a NegativeValueException. Note that creating a
HouseBunny invokes the constructor in Bunny. You should consider adding test methods to
check for negative values for the other numeric fields.
@Test public void negativeValueExceptionTest() {
boolean thrown = false;
try {
HouseBunny hb = new HouseBunny(“Spot”, “Mixed”, -0.08);
}
catch (NegativeValueException e) {
thrown = true;
}
Assert.assertTrue(“Expected NegativeValueException to be thrown.”,
thrown);
/* or alternatively: */
Assert.assertEquals(“Expected NegativeValueException to be thrown.”,
true, thrown);
}
• BunnyList.java
Requirements and Design: The BunnyList class provides methods for reading in the data file
and generating reports.
Design: In addition to the specifications in Bunnies – Part 2, the existing readBunnyFile method
must be modified to catch following: NegativeValueException, NumberFormatException, and
NoSuchElementException. When these exceptions occur, an appropriate message along with the
offending line/record should be added the excludedRecords array.
o readBunnyFile has no return value, accepts the data file name as a String, and has a
throws clause for FileNotFoundException. This method creates a Scanner object to read in
the file and then reads it in line by line. The first line of the file contains the name of the list,
and each of the remaining lines contains the data for a bunny. After reading in the list name,
the “bunny” lines should be processed as follows. A bunny line (or record) is read in, a
second Scanner is created on the line, and the individual values for the bunny are read in. Be
sure to “trim” each value read in. All values should be read as strings. Non-String values
should be “parsed” into their respective values using the appropriate wrapper class (e.g.,
Double.parseDouble(..)). After the values on the line have been read in, an “appropriate”
bunny object is created and added to the bunny array using the addBunny method. If the
Project: Bunnies – Part 3 Page 4 of 9
Page 4 of 9
bunny type is not recognized, the message “Invalid Bunny Category in:\n” and the
record/line should be added to the excluded records array using the addExcludedRecord
method. The data file is a “comma separated values” file; i.e., if a line contains multiple
values, the values are delimited by commas. So after you set up the Scanner for the bunny
lines, you need to change the delimiter to a “,” by invoking useDelimiter(“,”)on the
Scanner object. Each bunny line in the file begins with a category for the bunny. Your
switch statement should determine which type of Bunny to create based on the first character
of the category (i.e., P, H, J, and S for PetBunny, HouseBunny, JumpingBunny, and
ShowBunny respectively, ignoring case). The second field in the record is the name,
followed by breed, and weight, as well the values appropriate for the category of bunny
represented by the line of data. That is, the items that follow weight correspond to the data
needed for the particular category (or subclass) of Bunny. For each incorrect line scanned
(i.e., a line of data that contain missing data or invalid numeric data), your method will need
to handle the invalid items properly. If a line includes invalid numeric data (e.g., the value
for weight, a double, contains an alphabetic character), a NumberFormatException (see note
1, p. 8) will be thrown automatically by the Java Runtime Environment (JRE). Your
readBunnyFile method should catch and handle NumberFormatException,
NoSuchElementException (for missing values), and NegativeValueException (thrown in
bunny constructors when a negative value is passed in) as follows. In each catch clause, a
String consisting of the exception, the comment ” :\n”, and the line with the invalid data
should be added to the excludedRecords array. For example, in the catch clause for
NumberFormatException e, the String resulting from the following expression should be
added to the excludedRecords array.
e + ” in:\n” + line
The file bunnies2.txt is available for download from the course web site. Below are
example data records (the first line/record containing the bunny list name is followed by
bunny lines/records):
Bunny Collection
Pet bunny, Floppy, Holland Lop, 3.5
Pet bunny, Hopper, Holland Lop Mixed, -4.5
Pet bunny, Hopster, Holland Lop Mixed, 3..8
house Bunny, Spot, Really Mixed, 5.8, 0.15
mouse Bunny, Spots, Mixed, 0.8, 0.15
House Bunny, Spotster, Sorta Mixed, 5.8, -0.25
House Bunny, Sam, Mixed, 5.8
Jumper Bunny, Speedy, English, 6.3, 25.0
Jumper Bunny, Speeder, English, 6.1, -15.0
fighting bunny, Slugger, Big Mixed, 16.5, 21.0
Show bunny, Bigun, Flemish Giant, 14.6, 22.0
show bunny, Big John, Flemish Giant, 15.6, -20.1
Project: Bunnies – Part 3 Page 5 of 9
Page 5 of 9
• BunniesPart3.java
Requirements and Design: The BunniesPart3 class has only a main method as described below.
In addition to the specifications in Project 10, the main method should be modified to catch and
handle an FileNotFoundException if one is thrown in the readBunnyFile method of the
BunnyList class.
In Part 3, main reads in the file name from the command line as was done in Bunnies –
Part 2, creates an instance of BunnyList, and then calls the readBunnyFile method in the
BunnyList class to read in the data file. After successfully reading in the file, the main
method then prints the summary, bunny list by name, bunny list by cost, and the list of
excluded records. The main method should not include the throws
FileNotFoundException in the declaration. Instead, the main method should include a
try-catch statement to catch FileNotFoundException when/if it is thrown in the
readBunnyFile method in the BunnyList class. This exception will occur when an
incorrect file name is passed to the readBunnyFile method. After this
FileNotFoundException is caught in main, print the messages below and end.
*** File not found.
Program ending.
Also, if the user runs the program without a command line argument (e.g., args.length
== 0), main should print the following message and end.
*** File name not provided by command line argument.
Program ending.
An example data file can be downloaded from the Lab web page. The program output
for bunnies2.txt begins on the next page after the UML class diagram. See note 2 on p. 8
for testing your main method.
Project: Bunnies – Part 3 Page 6 of 9
Page 6 of 9
UML Class Diagram
Example Output for bunnies2.txt
MM«M —-jGRASP exec: java BunniesPart3 bunnies2.txt
MM§M——————————
MM§MSummary for Bunny Collection
MM§M——————————
MM§MNumber of Bunnies: 4
MM§MTotal Estimated Monthly Cost: $124.38
MM§M
MM§M
MM§M——————————
MM§MBunnies by Name
MM§M——————————
MM§M
MM§MBigun (ShowBunny) Breed: Flemish Giant Weight: 14.6
MM§MEstimated Monthly Cost: $62.15 (includes $22.00 for grooming)
MM§M
MM§MFloppy (PetBunny) Breed: Holland Lop Weight: 3.5
MM§MEstimated Monthly Cost: $6.48
MM§M
MM§MSpeedy (JumpingBunny) Breed: English Weight: 6.3
MM§MEstimated Monthly Cost: $40.75 (includes $25.00 for training)
MM§M
MM§MSpot (HouseBunny) Breed: Really Mixed Weight: 5.8
MM§MEstimated Monthly Cost: $15.01 (includes 15.0% for wear and tear)
MM§M
Project: Bunnies
– Part 3 Page
7 of
9
Page
7 of
9
MM§M
MM§M——————————
MM§MBunnies by Cost
MM§M——————————
MM§M
MM§MFloppy (PetBunny) Breed: Holland Lop Weight: 3.5
MM§MEstimated Monthly Cost: $6.4
8
MM§M
MM§MSpot (HouseBunny) Breed: Really Mixed Weight: 5.8
MM§MEstimated Monthly Cost: $15.01 (includes 15.0% for wear and tear)
MM§M
MM§MSpeedy (JumpingBunny) Breed: English Weight: 6.3
MM§MEstimated Monthly Cost: $40.75 (includes $25.00 for training)
MM§M
MM§MBigun (ShowBunny) Breed: Flemish Giant Weight: 14.6
MM§MEstimated Monthly Cost: $62.15 (includes $22.00 for grooming)
MM§M
MM§M
MM§M——————————
MM§MExcluded Records
MM§M——————————
MM§M
MM§MNegativeValueException: Numeric values must be nonnegative in:
MM§MPet bunny, Hopper, Holland Lop Mixed,
-4.5
MM§M
MM§Mjava.lang.NumberFormatException: multiple points in:
MM§MPet bunny, Hopster, Holland Lop Mixed, 3..8
MM§M
MM§MInvalid Bunny Category in:
MM§Mmouse Bunny, Spots, Mixed, 0.8, 0.15
MM§M
MM§MNegativeValueException: Numeric values must be nonnegative in:
MM§MHouse Bunny, Spotster, Sorta Mixed, 5.8,
-0.25
MM§M
MM§Mjava.util.NoSuchElementException in:
MM§MHouse Bunny, Sam, Mixed, 5.8
MM§M
MM§MNegativeValueException: Numeric values must be nonnegative in:
MM§MJumper Bunny, Speeder, English, 6.1,
-15.0
MM§M
MM§MInvalid Bunny Category in:
MM§Mfighting bunny, Slugger, Big Mixed, 16.5, 21.0
MM§M
MM§MNegativeValueException: Numeric values must be nonnegative in:
MM§Mshow bunny, Big John, Flemish Giant, 15.6,
-20.1
MM§M
MM§M
MM©M —-jGRASP: operation complete.
Example Output for bunnies_not_a_real_file
.txt
MM«M —-jGRASP exec: java BunniesPart3 bunnies_not_a_real_file.txt
MM§M*** File not found.
MM§MProgram ending.
MM©M —-jGRASP: operation complete.
Project: Bunnies – Part 3 Page 8 of 9
Page 8 of 9
Example Output for missing command line argument
MM«M —-jGRASP exec: java BunniesPart3
MM§M*** File name not provided by command line argument.
MM§MProgram ending.
MM§M
MM©M —-jGRASP: operation complete.
Notes
1. Exceptions for numeric items – This project assumes that you are reading each double value as
String using next() and then parsing it into a double with Double.parseDouble(…) as shown in the
following example.
. . . Double.parseDouble(myInput.next());
This form of input will throw a java.lang.NumberFormatException if the value is not an double.
If you are reading in each double value as a double using nextDouble(), for example
. . . myInput.nextDouble();
then a java.util.InputMismatchException will be thrown if the value read is not a double. Since an
InputMismatchException is a subclass of NoSuchElementException, this exception will be caught in
your catch clause for NoSuchElementException but will be reported as an InputMismatchException.
Web-CAT is looking for NumberFormatException rather than InputMismatchException.
Therefore, you must use Double.parseDouble(…) as described above to in order to pass the tests
in Web-CAT.
2. Testing your main method – You will need three test methods for BunniesPart3Test.java: (1) test
with a good file name, (2) test with a bad file name, and (3) test with no file name (i.e., the user did
not provide a command line argument). In the latter two cases, the bunnyCount should be zero after
calling the main method in BunniesPart3.
3. General note on test files – The data files for Part 2 (bunnies1.txt) and Part 3 (bunnies2.txt) have
been uploaded in Web-CAT. If you have test methods that read bunnies1.txt, you can retain these
and then add new test methods that read bunnies2.txt as needed.
4. Static count in Bunny – If you are incrementing bunnyCount in the Bunny constructor you may find
it necessary to decrement bunnyCount when a Bunny subclass detects a negative value. The
bunnyCount should be decremented within any subclass constructor that has an if statement checking
for a negative value. That is, if true, bunnyCount should be decremented then the program should
have a statement: throw new NegativeValueException();
This will abandon the constructor and the object will not be created.
5. NegativeValueException exception is thrown in one of the subclass constructors. The exception
causes the constructor to end, and thus the instance is not created. However, since the call to the
super constructor incremented bunnyCount before the exception in a subclass, bunnyCount should be
decremented in the subclass where the NegativeValueException exception is thrown.
Project: Bunnies – Part 3 Page 9 of 9
Page 9 of 9
6. Skeleton Code (ungraded) – You can submit to this Web-CAT assignment to check the coverage of
your test methods. Just submit your project test files along with your project source files.
7. Assert.assertArrayEquals – When the Assert.assertArrayEquals is used in a JUnit test method, it
does an element by element compare of the arrays using the equals method with type Object as the
parameter. Thus, it is important that the equals method in the Bunny class overrides the equals
method inherited from the Object class and that it is working correctly. Otherwise, using
Assert.assertArrayEquals will likely not work properly. For example, if you have not overridden the
equals method from the Object class, since the elements are objects rather than primitives, the
addresses are compared instead of the objects’ fields themselves. This means that you are likely get a
false since the addresses are only equal if the references are aliases. Therefore, to make the
Assert.assertArrayEquals work for arrays of Bunny, the equals method inherited from Object should
be overridden in the Bunny class.