Skip to content

HW7: File I/O

Learning Objectives

  • Demonstrate understanding in reading from and writing to the file system (i.e., file input and output).
  • Apply packages and develop unit testing.

Overview

In this assignment, you will apply your recently learned Java file I/O skills.

The Components to be Written

You must write the ProductFileProcessor and CSVFileProcessor classes perform input/output operations on comma-separated-value (CSV) files. The relationship between these components is illustrated in the following UML class diagram.

The ProductFileProcessor Class

In HW6, the Teethbrush and ElectricTeethbrush classes have been implemented. The ProductFileProcessor in HW7 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.

  1. 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).
  2. All objects used for reading and writing must be closed when they are no longer needed.
  3. The writeTeethbrush() method must write the inventory, hardness, and polished attributes using "%d,%d,%b\n" as the format String
  4. The writeElectricTeethbrush() method must write the inventory, hardness, polished, rechargeable and ultrasonic attributes using "%d,%d,%b,%b,%b\n" as the format String
  5. The readTeethbrush() method must read a file written by the writeTeethBrush() method, construct and initialize a Teethbrush object, and return it.
  6. The readElectricTeethbrush() method must read a file written by the writeElectricTeethBrush() method, construct and initialize an ElectricTeethbrush object, and return it.

The CSVFileProcessor Class

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

The read() method must read all the records from the file passed to the constructor. The method assumes that it is passed in the format of line-oriented, comma-separated-valued (CSV) with the same number of elements as the number of records (lines) in the file.

The first line of the file contains an integer indicating the number of CSV data records (lines) to read. Any additional lines in the file are ignored. For each record (line) in the file, the method reads and parses the CSV data. The read() method returns a 2D array of strings, representing the extracted elements from the file.

The write() method must take a 2D array of strings (representing records(lines) of CSV data) and write them to the file. The method should:

  1. Write the number of records(lines) (the length of the 2D array) as the first line in the file passed to the constructors.
  2. Follow that by writing each row from the 2D array as a comma-separated string, with each record written on a new line.

Example Input for write()

String[][] data = {
    {"p", "q", "r"},
    {"s", "t", "u"},
    {"v", "w", "x"}
};

Content of resulting CSV file:

3
p,q,r
s,t,u
v,w,x

Unit Testing

You must write JUnit tests for all of your classes. Your JUnit test suite must cover all statements and all branches 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.

To test the read methods in your classes 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.

Testing the read() Method in CSVFileProcessor

To test the read() method which returned a 2D array, in the CSVFileProcessor class, you are required to implement a helper method assertEquals2D in CSVFileProcessorTest.java. This helper method ensures a thorough comparison of two 2D arrays when testing the read() method. The method should be declared as follows:

/**
 * Asserts that two 2D string arrays are deeply equal.
 *
 * @param expected the expected array
 * @param actual   the actual array
 */
public static void assertEquals2D(String[][] expected, String[][] actual) {
    // Add your code here
}

Implementation Requirements for assertEquals2D

  • Row Count Check: if the two arrays have different numbers of rows, use JUnit's fail() assertion with the message: Arrays have different number of rows
  • Column Count Check: If any row in the arrays has a different number of columns, use fail() with the message: Arrays have different number of columns in row %d, where %d is replaced with the index of the row.
  • Value Comparison: If both arrays have the same dimensions, compare values cell by cell. Use JUnit assertEquals to assert the two arrays have same values at each cell. If a mismatch is found at position [i][j], the error message should be Arrays differ at position [%d][%d], replacing %d placeholders with the corresponding row and column indices.

Submission

You must submit (using Gradescope):

  1. Your implementations of the ProductFileProcessor and CSVFileProcessor classes, and the corresponding JUnit test files ProductFileProcessorTest and CSVFileProcessorTest.
  2. In addition to other tests, CSVFileProcessorTest must include the assertEquals2D test.

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.

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.

Get Started

  1. Read and understand the entire assignment.
  2. Create a folder for this assignment named hw7. Then create packages (in the hw7) named io and testing.
  3. Ensure your code follows the provided style guide using available tools in VSCode.

Important: Package Declaration

The files should be placed in packages named io or testsing inside a directory named hw7.

For example, you should have a directory structure like this in your VS code:

hws/
├── hw6/
│   └── product/
│       ├── ElectricTeethbrush.java
│       └── Teethbrush.java
├── hw7/
│   └── io/
│       ├── ProductFileProcessor.java
│       └── CSVFileProcessor.java
│   └── testing/
│       ├── ProductFileProcessorTest.java
│       └── CSVFileProcessorTest.java

Implement ProductFileProcessor and CSVFileProcessor classes

Since you will need to use Teethbrush and ElectricTeethbrush in ProductFileProcessor class, you must first import them when you implement ProductFileProcessor class.

Assuming that you have Teethbrush.java and ElectricTeethbrush.java in hw6 folder, you should be able to put following two lines before the class declaration in ProductFileProcessor class:

import hws.hw6.product.Teethbrush;
import hws.hw6.product.ElectricTeethbrush;

If you do not have a completed Teethbrush.java and ElectricTeethbrush.java from hw6, please contact your instructor.

Questions to Think About

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

  1. The write() method in CSVFileProcessor writes a 2D array of strings to a file. Why is it important to first write the number of rows, and how does this help when reading the file back?