
The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
ExchangeCourseMapper class (referred to as Main in diagram) is responsible for the app launch and shut down.
The bulk of the app’s work is done by the following four components:
UI: The UI of the App.
Parser: The command executor and logic checker.
Command: Details and implementation of various commands/features of the app.
Storage: Reads data from, and writes data to, mylist.json.
How the architecture components interact with each other
Only some of the commands will need all 4 main components for example the Delete course command:
The Sequence Diagram below shows how the components interact with each other for the scenario
where the user issues the command delete 1.

The UI, Parser and Storage components (also shown in the diagram above),
The Command component,
abstract class with the same name as the Component.CheckInformationCommand and PersonalTrackerCommand as child classesFor example, the Command component defines its API in the Command.java abstract class and extends its functionality using the
PersonalTrackerCommand.java and CheckInformationCommand class. Other components such as ListSchoolsCommand and DeleteCoursesCommand
interact with a given component through its interface rather than the concrete class
(reason: to prevent outside component’s being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.

The sections below give more details of the components and any additional components.

The abstract command classes for our two main features, CheckInformationCommand and PersonalTrackerCommand.
must inherit from the abstract Command class, which has the method to read the Json file containing course
information. Each child class has the abstract execute method, to be implemented by their concrete child classes,
specific to the command functionality.
Addition of new command features
When adding new command classes, developers should make sure that the concrete class inherits from either the
CheckInformationCommand or PersonalTrackerCommand classes, depending on the new command’s functionality.
The new command class should also have an implementation of the execute method, and uses the UI class to
print messages on the CLI interface.

CheckInformationCommands list out information from the JSON file containing course mapping information, or from the
user’s Personal Tracker.
Possible CheckInformationCommands are:
ListCommandsCommandHelpCommandListSchoolsCommandListUniCoursesCommandObtainContactsCommandFilterCoursesCommandListPersonalTrackerCommandCompareMappedCommandThe above list of Command classes are concrete classes, and must implement the abstract
execute(userInput) method.
![]()
PersonalTrackerCommands are commands that can modify the user’s saved mapping plans from the Personal Tracker,
as well as access the list of saved mappings.
Possible CheckInformationCommands are:
AddCoursesCommandDeleteCoursesCommandFindCoursesCommandThe above list of Command classes are concrete classes, and must implement the abstract
execute(userInput, storage) method.
The Parser class is responsible for handling and interpreting user input in the application. It reads user commands, processes them by splitting the input into command keywords and parameters, and then directs the flow of control to the appropriate command classes to execute the specified actions.
As ExchangeCourseMapper is mainly split into two main parts; Check Information and Personal Tracker. We decided to
split the Parser class diagram into the two parts respectively to increase readability and neatness.
This class diagram represents the parsing of commands in Check Information. The multiplicities dependencies of the command are 0 or 1 because the dependency is only formed when the command is called, else, it will be zero.

This class diagram represents the parsing of commands in Personal Tracker. The Parser class has associations with the
UI class and Storage class and dependencies with the commands in Personal Tracker.
![]()


Storage System Overview
The storage system is structured to manage the persistence and integrity of course data for the application. It consists of the following key components:
Storage Class: The main interface for adding, deleting, retrieving, and loading courses. It interacts with
CourseRepository for data persistence, ensuring all course-related actions are directed to the appropriate storage
location.
CourseRepository: Acts as an intermediary layer, managing data access and defining the file path
(MYLIST_FILE_PATH) for myList.json. It provides similar methods as Storage to add, delete, and retrieve course
data, facilitating structured data storage.
FileHandler: Responsible for file operations such as initializing, reading, writing, and appending to files. This class abstracts file manipulation, ensuring the storage system can perform reliable file operations without directly managing file-specific details.
DataIntegrityChecker: Ensures the integrity of the stored data by validating file structure and content. It provides methods for validating the entire file or individual lines, helping maintain consistent data formatting and correctness.
CourseValidator: Validates course inputs to check if they meet predefined criteria before being stored. This ensures that only valid data is added to the repository, reducing the likelihood of errors in the data storage.
UI: Responsible for providing feedback to the user regarding any invalid entries or formats, enhancing user experience by notifying them of issues in a clear and informative way.
Each component is designed to separate concerns, with Storage focusing on high-level operations, CourseRepository
on data organization, FileHandler on file I/O, and DataIntegrityChecker on data validation. This modular design
allows for maintainability, testability, and scalability in managing persistent course data.

For commands that read through our data source file which contains university data, the process will be done in the
Command class via a createJsonObject() method,
where an IOException message will be displayed if reading fails.
The ListCommandsCommand provides users with a comprehensive list of all available commands in the CLI. This is particularly useful for new users or those unfamiliar with specific command formats.
ListCommandsCommand class extends the CheckInformationCommand superclass and overrides the execute method.execute method, printCommandList from the UI class is called.LINE_SEPARATOR is used before and after the list to create a visually distinct section in the CLI.ListCommandsCommand with the HelpCommand to provide a one-stop command for help-related requests, but separating them ensures clarity and keeps each command focused.
This command provides users with detailed explanations of each feature and the ways to use them. This allows users to navigate this program easily and effectively.
HelpCommand class extends the CheckInformationCommand class where it overrides
the execute method for custom behaviour.getCommand() method which extracts and processes the
command. It does so by using switch statements to determine if the input matches one of the valid commands.IllegalArgumentException exception will be thrown to handle
invalid commandsprintHelp() method will be called to display the detailed help messages for the specific command.
Another switch statement is used here to map each command to its corresponding help message.getCommand() parses and
validates the input to extract a specific command and printHelp() prints the relevant help message for the
parsed command.switch statement is an efficient way to match valid commands.
switch statements are also clearer and easier to read.if-else statement
switch statements
This command is responsible for displaying and retrieving the full list of universities from our data source file which contains university data. It helps the users to identify the possible choices in Oceania.
ListSchoolCommand class extends the CheckInformationCommand class where it overrides the execute method for
custom behaviour.fetchSchoolData() method from a JSON file to obtain the names via
createJsonObject() method from the superclass.validateJsonObject() method.displaySchoolList() method will iterate over the keys of the database which contains the University
names, upon acquiring the keys, they will be printed over the CLI.displaySchoolList() and execute() are separated for ease of debugging and code tracing. This is
also in line with the idea that each method has a responsibility of its own.execute method but kept SLAP in mind to ensure
readability.
This command is responsible for listing out all the mappable partner university’s (PU) courses and NUS courses. This allows users to plan their course mapping as it lists out all the possible courses they can map in a specified partner university.
ListUniCoursesCommand class extends the CheckInformationCommand class where it overrides the execute method
for custom behaviour.list courses [PU_NAME]).getUniCourses() method which will search for the specified PU in the
JsonObject with findUniversityName().UnknownUniversityException will be thrown.listCourses() will be called. Then getUniversityObject() and getCourseArray() methods
will be called to get the JsonObject containing the PU and the JsonArray containing the list of courses it offers.iterateCourses() method to iterate through the JsonArray courseArray which
contains the list of courses.printListUniCoursesCommand method in the UI class.getPuName() focuses on extracting the university name from user input and findUniversityName()
focuses on searching the university in the data set.
The command is responsible to retrieve the email contact and contact number data for a specified partner university. It helps users to reach out to the partner universities for any enquiries about programs or exchange opportunities.
ObtainContactsCommand class extends the CheckInformationCommand class where it overrides the execute()
method for custom behaviour.createJsonObject() method from the
superclass.getSchoolName() and getContactType() methods are used to parse the user input, extracting the requested
university name and contact type (email or phone number).findMatchingSchool() method identifies the correct university entry within the JSON data.isSchoolValid() method inside the SchoolContactValidator class is used to check if the
school name exists.checkValidContact() method checks the validity of the contact type through a
handler isValidContactType() in SchoolContactValidator class.contactTypeIdentifier() method then checks retrieves the contact type and displays the contact information via
theprintContactInformation() in the UI class.execute method is essential and unique to every command class so inheritance was used.SchoolContactValidator) is used to handle
the validity of each input category. This is in line with the separation of concerns where each method has a
responsibility.execute method but kept SLAP in mind to ensure
readability.
This command is responsible for displaying and retrieving the full list of mappable courses from the partner universities to a user specified NUS course from our data source file which contains university data. It helps the users to identify whether that NUS course is suitable to be mapped overseas in Oceania.
FilterCoursesCommand class extends Command class where it overrides the execute method for
custom behaviour.createJsonObject() method from the
superclass.parseFilterCommand method then separates input, which parses the user input to extract the details in the input,
still of String type.getNusCourseCode method then extract out the user specified NUS course code from the parsed input, checking
if the course code is a School of Computing course or follows the format of a NUS course code, using the methods
isValidSocCourseCode and isValidNusCourseCodeFormat methods in the NusCourseValidator class.displayMappableCourses() method along with teh Json object. The method
will iterate over the keys of the database which contains the University names, then obtain the array courses
stored in the “courses” field. The courses array is then iterated over, for each course,
if the value in the “nus_course_code” is equals to the NUS course code passed into the method, the university name and
“pu_course_code” value of the course will be printed to the CLI.NusCourseCodeValidator class, separate from the methods used in the FilterCoursesCommand class, which
are mainly used for the logic behind the filtering of courses.
This command is responsible for adding users’ desired course mapping into the myList.json file.
Additionally, each course mapping is checked against the current course mappings found in
our database, to ensure that the course mapping is accurate and is limited
to universities in Oceania. This command hence helps the users to keep track of their course mapping process.
AddCoursesCommand class extends Command class where it overrides the execute method for
custom behaviour.createJsonObject() method from the
superclass.trimString method then removes the add command and checks whether the user gave any input after the command.
The method would return the trimmed user’s input without the add command.parseAddCommand() method to obtain the relevant information: NUS course code,
name of partner university and partner university course code.getUniversityAbbreviations() method found in the
Parser class to convert any partner university abbreviations to their full form.createJSONObject() method, the information extracted from the
parseAddCommand() method would be passed to the isValidInput() method to verify the user’s course mapping.CourseValidator class.getPUCourseList() method is called to verify if the user’s partner university is
included in the database. An exception is thrown if the university is not found in the database.isValidCourseMapping() method checks whether the NUS course code and PU course code are compatible
for course mapping against the database.myList.json file.CourseValidator class to enhance OOP
and improve readability.
Sequence Diagram of AddCourseCommand

Sequence Diagram of Course Validator (extracted out of AddCourseCommand sequence diagram)
This command is responsible for deleting users’ existing course mapping plan from the myList.json file.
This helps the users to keep track of their most recent course mapping plans, and to keep the myList.json file organised.
DeleteCoursesCommand class extends Command class where it overrides the execute method for
custom behaviour.execute is called, the command first passes the user’s input into the parseDeleteCommand method, which parses
the user input to extract the list index, still of String type, of the course mapping plan
they would like to delete.getListIndex method. The list index is then converted to an int using the
Integer class. If a valid list index format has been given by the user, the list index is returned.deleteCourse method, which calls the getCourse
method from the Storage object, and accesses the line in the myList.json file as specified by the list index to
obtain information of the course to delete, then deletes the course using the deleteCourse method from the
Storage object.printDeleteMessage is called to inform the user of the course plan which is deleted.list mapped command, which shows the index of the saved course mapping plans.
The ListPersonalTrackerCommand is responsible for listing all the mapped modules stored in the user’s personal tracker.
This command retrieves all stored courses from myList.json via the Storage class and displays them in an indexed list format on the CLI.
ListPersonalTrackerCommand class extends CheckInformationCommand and overrides the execute method to define custom behavior.Storage object to access stored course mappings.execute Method:
myList.json using courseRepository.isFileValid and courseRepository.hasDuplicateEntries. If the data integrity fails, the command exits without further execution.loadAllCourses from the Storage class to retrieve the list of mapped modules.mappedModules, printing each course with an index for readability by calling printMappedModules from the IU class.![]()
The CompareMappedCommand is responsible for comparing course mappings between two specified partner universities.
This command aids users in identifying common course mappings across the selected universities, as well as highlighting
unique course mappings specific to each university.
The CompareMappedCommand class extends CheckInformationCommand and overrides the execute method to define its custom behavior.
Below is an outline of the execution flow:
myList.json using courseRepository.isFileValid and courseRepository.hasDuplicateEntries. If the data integrity fails, the command exits without further execution.pu/ to retrieve the names of the two universities specified by the user.
printInvalidInputFormat method in the UI class is called to inform the user of incorrect input format.isValidUniversity to check if the input is a valid university. Otherwise, it will print an error message with the wrong university name and a suggestion.myList.json file through the Storage class.null through assertions.filterModulesByUniversity method takes the list of all modules and filters out only those associated with the specified university.extractCourseCodes method extracts the unique course codes for each university, enabling the subsequent comparison.getCommonCourseCodes method calculates the intersection of course codes between the two universities, identifying courses available in both.getUniqueCourseCodes method identifies unique courses by excluding the common course codes for each university.displayComparisonResults method provides output for the comparison:
UI class for better readability and user experience.execute: Displaying results directly in the execute method was an option. However, using dedicated methods (e.g., displayComparisonResults) improved readability and made testing individual components easier.
This command is responsible for the searching of a particular NUS course in the personalised tracker. This allows users to check and plan course mappings for that specified course.
FindCoursesCommand class extends the CheckInformationCommand class where it overrides the execute method for
custom behaviour specific to this class.getKeyword() method to extract out the keyword(NUS course code)
to search within the personalised tracker. If there is no keyword, an IllegalArgumentException will be thrown.findCommand() method.findCommand method, the mappings in the tracker are retrieved through
List<Course> mappedCourses = storage.loadAllCourses(). If the tracker is empty, a message indicating empty tracker
will be printed.matchKeyword() will be called, and it iterates the mappedCourses in the tracker to search for mappings that
match the keyword and adds them into a List<Course> foundCourses.printFindCommand will iterate and print the course mappings inside mappedCourses through
printFoundCourses() in UI class. If mappedCourses is empty, an IllegalArgumentException is thrown.UI class handles displaying messages to the user, which keeps
FindCoursesCommand focused solely on search logic, without managing user interactions directly.
| Version | As a … | I want to … | So that I can … |
|---|---|---|---|
| v1.0 | CEG student | see the possible Oceania Universities for CEG students | see all my possible choices in that region |
| v1.0 | CEG student | search for NUS courses to map | search for related courses in PUs |
| v1.0 | CEG student | key in the school I want to go for exchange | view the available course offered by the school |
| v1.0 | CEG student | want to see a list of commands | know what to do to go to access the features |
| v2.0 | CEG student | obtain the email address of the partner universities | send an email should I have any non-urgent queries |
| v2.0 | CEG student | obtain the contact number of the partner universities | call the number should I have any urgent queries |
| v2.0 | CEG student | add a course mapping plan for a PU | keep track of my courses for a specific PU |
| v2.0 | CEG student | list out the mapped courses by calling the list command | I can track all the courses I have mapped to the different PUs |
| v2.0 | CEG student | delete a course mapping plan for a PU | keep my list of saved plans organised |
| v2.0 | CEG student | ask for help when I am in doubt | know what are the possible actions |
| v2.0 | CEG student | compare different mapping plans for each PU | find the university best fit for my academic schedule |
| v2.0 | CEG student | search for course mappings in my personalised tracker | check if I have mappings for that course |
[NOTE!] These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
[NOTE!] SCHOOL_NAME inputs for the commands:
list courses,obtain,add,compareare not case-sensitive, and you can either input the University’s full name, or its abbreviation.
commands commands command
help add list schools list courses The University of Western Australia list courses uwa list courses tokyo university obtain The University of Melbourne /emailobtain The University of Melbourne /numberobtain tokyo university /email or obtain tokyo university /numberfilter cs3241filter gess1010filter eeeeeeeeeeeadd CS2040 /pu The university of western australia /coursepu CITS2200add cs3241 /pu unimelb /coursepu comp30019add invalid formatadd cs2040 /pu invalid uniadd cs2040 /pu the university of western australiaadd CS1231 /pu the university of western australia /coursepu CITS2200add CS2040 /pu the university of western australia /coursepu CITS1111NUS COURSE | PU COURSE.add CS2040 /pu the university of australia /coursepu CITS2200delete 1delete 100[NOTE!] The tests below would require the tester to corrupt the
myList.jsonfile stored in the data folder (found in the directory where the tester’s terminal is running from).To corrupt data: At least one saved course mapping plan saved in myList.json, then remove any of the information (NUS course code, Partner University’s name or the course that it offers which is mappable to the NUS course code).
To fix the file: Revert all changes, and make sure that there is no new line after the last course mapping plan.
list mappedlist mappedlist mappedcompare pu/the university of melbourne pu/the university of western australiacompare pu/the university of melbourne pu/the university of western australiacompare pu/the university of melbourne pu/the university of western australiaCS2040 | The university of western australia | CITS2200find cs2040find cs2040find csfind