diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b88330f554..399a25215a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -27,8 +27,8 @@ The key classes providing functionality to TutorLink are: 2. Ui: Collects data (via Strings sent via CLI) from the user and relays information to the user (via printing back to the CLI). 3. Parser: Interprets the raw data from the user; applies data validation and handles necessary exceptions. -4. Storage: Serves as long-term storage of data to be retained even after TutorLink is shut down. -5. CommandResult represents the result of user input. +4. Storage: Handles the loading and storage of data to be retained even after TutorLink is shut down. +5. CommandResult: Represents the result of user input. ### Target User Profile @@ -110,8 +110,8 @@ The flow of logic for both `Student` and `Component` commands can be summarized 1. Retrieve arguments from `HashMap`. 2. Execute data validation on the arguments and throw appropriate exception in the case of failure. -2. Add/Delete `Student` and `Component`. -3. Return `CommandResult` that contains the result of the Add/Delete operation. +3. Add/Delete `Student` and `Component`. +4. Return `CommandResult` that contains the result of the Add/Delete operation. The following sequence diagrams depict the exact steps involved in the `AddStudentCommand`: @@ -168,19 +168,23 @@ The sequence diagram of the DeleteGradeCommand is shown below. ![DeleteGradeCommand.png](diagrams%2FDeleteGradeCommand.png) -### Storage feature +### Storage Load feature -#### Implementation +The `StudentStorage`, `GradeStorage` and `ComponentStorage` classes implement the feature to load data from the +data `.txt` files into their respective List objects at the start of the program. -The `Storage` class is responsible for the automatic loading and saving of list data to and from `.txt` files, -so that data will be retained between runs of the application. +The load list methods for the Storage classes have largely similar logic flows. +The following section and sequence diagram elaborate on the implementation of the `loadGradeList` method in `GradeStorage`: -#### Example Usage Scenario - -Step 1: The user launches the application. During startup, the `main` method calls constructors for `Storage` objects -for each of `StudentList`, `ComponentList` and `Gradelist`. - -Step 2: The predefined filepaths are passed into the constructor. The directory and file are created if they do not -currently exist. +![GradeStorage.png](diagrams/GradeStorage.png) - +1. TutorLink constructs a new `GradeStorage`. +2. TutorLink calls `loadGradeList`. +3. `GradeStorage` creates a new `ArrayList` of `Grade`s. +4. `GradeStorage` creates a new `Scanner` with the path to the file. +5. While there are next lines in the data file: + - `Scanner` returns the current file line as a String and moves to the next file line. + - `GradeStorage` calls its `getGradeFromFileLine` method with the file line. + - If the file line references a valid `Component` and a valid `Student`, a `Grade` is returned and added to the `ArrayList`. + - If not (e.g. if data file was corrupted), the file line is simply ignored, and the loop continues to the next iteration. +6. The `ArrayList` of `Grade`s is returned to TutorLink. diff --git a/docs/diagrams/GradeStorage.png b/docs/diagrams/GradeStorage.png new file mode 100644 index 0000000000..1b353aabae Binary files /dev/null and b/docs/diagrams/GradeStorage.png differ diff --git a/docs/diagrams/GradeStorage.puml b/docs/diagrams/GradeStorage.puml new file mode 100644 index 0000000000..ec5ac6cd7d --- /dev/null +++ b/docs/diagrams/GradeStorage.puml @@ -0,0 +1,55 @@ +@startuml + +!include Style.puml +participant ":TutorLink" as TL LOGIC_COLOR_5 +participant "gradeStorage: GradeStorage" as GS LOGIC_COLOR_4 +participant "grades: ArrayList" as AL LOGIC_COLOR_6 +participant "fileScanner: Scanner" as FS LOGIC_COLOR_7 + +create GS +TL -> GS: new GradeStorage(GRADE_FILE_PATH, initialComponentList, initialStudentList) +activate GS +TL <-- GS: gradeStorage: GradeStorage +deactivate GS + +TL -> GS: loadGradeList() +activate GS + +create AL +GS -> AL: new ArrayList() +activate AL +GS <-- AL: grades: Grade +deactivate AL + +create FS +GS -> FS: new Scanner(path: String) +activate FS +GS <-- FS: fileScanner: Scanner +deactivate FS + +loop fileScanner.hasNext() + GS -> FS: nextLine() + activate FS + GS <-- FS: currentLine: String + deactivate FS + GS -> GS: getGradeFromFileLine(currentLine: String) + activate GS + GS --> GS: newGrade: Grade + deactivate GS + + alt success + GS -> AL: add(newGrade) + activate AL + GS <-- AL + deactivate AL + else InvalidDataFileLineException + end + +end + +destroy FS + +TL <-- GS: grades: Grade +deactivate GS + +@enduml diff --git a/src/main/java/tutorlink/exceptions/InvalidDataFileLineException.java b/src/main/java/tutorlink/exceptions/InvalidDataFileLineException.java new file mode 100644 index 0000000000..85e1b18c62 --- /dev/null +++ b/src/main/java/tutorlink/exceptions/InvalidDataFileLineException.java @@ -0,0 +1,7 @@ +package tutorlink.exceptions; + +public class InvalidDataFileLineException extends TutorLinkException { + public InvalidDataFileLineException(String message) { + super(message); + } +} diff --git a/src/main/java/tutorlink/storage/GradeStorage.java b/src/main/java/tutorlink/storage/GradeStorage.java index 1fa483eb2d..f154677b50 100644 --- a/src/main/java/tutorlink/storage/GradeStorage.java +++ b/src/main/java/tutorlink/storage/GradeStorage.java @@ -1,6 +1,7 @@ package tutorlink.storage; import tutorlink.component.Component; +import tutorlink.exceptions.InvalidDataFileLineException; import tutorlink.grade.Grade; import tutorlink.student.Student; @@ -24,29 +25,10 @@ public ArrayList loadGradeList() ArrayList grades = new ArrayList<>(); Scanner fileScanner = new Scanner(path); while (fileScanner.hasNext()) { - String[] stringParts = fileScanner.nextLine().split(READ_DELIMITER); - String componentName = stringParts[0]; - String matricNumber = stringParts[1]; - double score = Double.parseDouble(stringParts[2]); - - Component selectedComp = null; - for (Component comp : componentList) { - if (comp.getName().equals(componentName)) { - selectedComp = comp; - break; - } - } - Student selectedStudent = null; - for (Student student : studentList) { - if (student.getMatricNumber().equals(matricNumber)) { - selectedStudent = student; - break; - } - } - - if (selectedComp != null && selectedStudent != null) { - Grade newGrade = new Grade(selectedComp, selectedStudent, score); - grades.add(newGrade); + try { + grades.add(getGradeFromFileLine(fileScanner.nextLine())); + } catch (InvalidDataFileLineException e) { + // ignore invalid data file entries } } return grades; @@ -60,6 +42,35 @@ public void saveGradeList(ArrayList grades) throws IOException { fileWriter.close(); } + private Grade getGradeFromFileLine(String fileLine) throws InvalidDataFileLineException { + String[] stringParts = fileLine.split(READ_DELIMITER); + String componentName = stringParts[0]; + String matricNumber = stringParts[1]; + double score = Double.parseDouble(stringParts[2]); + + Component selectedComp = null; + for (Component comp : componentList) { + if (comp.getName().equals(componentName)) { + selectedComp = comp; + break; + } + } + + Student selectedStudent = null; + for (Student student : studentList) { + if (student.getMatricNumber().equals(matricNumber)) { + selectedStudent = student; + break; + } + } + + if (selectedComp != null && selectedStudent != null) { + return new Grade(selectedComp, selectedStudent, score); + } else { + throw new InvalidDataFileLineException("Invalid grade"); + } + } + private String getFileInputForGrade(Grade grade) { String componentName = grade.getComponent().getName(); String matricNumber = grade.getStudent().getMatricNumber();