tp

Developer Guide

  1. Acknowledgements
  2. Design
  3. Implementation
  4. Appendix: Requirements
  5. Appendix: Instructions for Manual Testing

Acknowledgements

We would like to thank our TA Nigel Yeo, Prof Akshay and the CS2113 Team for their guidance.

Design

Architecture

The Internity application adopts layered architecture where responsibilities are divided among UI, Logic, Model and Storage related components. It also follows the Command Pattern for handling user actions. This design separates concerns clearly, allowing for modular, maintainable and extensible code.

The Architecture Diagram below explains the high-level design of the Internity application.
Architecture Diagram

The diagram below shows a simplified Class Diagram of all of Internity’s classes and their relationships.
(Ui class omitted for simplification as most classes are dependent on it.) Internity Class Diagram

Layers

  1. Controller
    • Classes: InternityManager
    • Responsibilities:
      • Launches and shuts down the application.
      • Receives input from the user and delegates parsing to the Logic layer.
      • Commands executed by Logic layer may modify the Model or trigger UI updates.
      • It also handles reading from and writing to the Storage layer.
      • Simplifies interactions between all layers and maintains a clear separation of concerns.
  2. UI (User Interface)
    • Classes: Ui, DashboardUi
    • Responsibilities:
      • Handles all user-facing output (printing, dashboards, etc.).
      • Does not perform any logic or state changes.
      • Displays information passed from the Logic or Model layers in a user-friendly format.
      • Invoked by Commands to show feedback or results.
  3. Logic
    • Classes: CommandParser, CommandFactory, ArgumentParser, Command subclasses
    • Responsibilities:
      • Acts as the intermediary between user input and Model operations.
      • Parses and validates user commands.
      • Constructs the appropriate Command object through the CommandFactory.
      • Executes commands, which modify the Model or trigger the UI to display information.
  4. Model
    • Classes: InternshipList, Internship, Date, Status
    • Responsibilities:
      • Stores internship data
      • Provides operations like adding, deleting, updating, finding or listing internships.
      • Completely independent of UI and input logic.
  5. Storage
    • Classes: Storage
    • Responsibilities:
      • Reads and writes internship data to persistent storage (e.g., file system).
      • Keeps data consistent between sessions.
      • Used by InternityManager to save or load application state.

User Interaction

The Sequence Diagram below shows a simplified version of how the components interact with each other when the user issues the command delete 1. User Interaction: Sequence Diagram


UI Component

Overview

The UI component is responsible for all interactions between the user and the application. It displays messages, prompts, and formatted lists in the command-line interface (CLI), and ensures that feedback from executed commands is presented clearly.

The API of this component is specified in the Ui.java class and the DashboardUi.java.

UI Component Diagram

How it Works

  1. The InternityManager handles all user input through a Scanner. When a command is executed, it delegates output responsibilities to the Ui class.
  2. The Ui component formats and prints the messages or internship data to the console. For example:
    • Ui.printAddInternship() displays confirmation for a newly added internship.
    • Ui.printFindInternship() displays results in a neat, column-aligned format.
  3. For specialised displays such as the dashboard, the DashboardUi class is used.

Design Considerations


Logic Component

Overview

The Logic component is responsible for:

Chosen Approach

This component follows the Command Pattern, which decouples the user input parsing from the execution of commands. Each command is represented as a subclass of the abstract Command class, encapsulating its execution logic. This allows new commands to be easily added without modifying the core parsing or execution workflow.

Class Diagram

Logic Component: Class Diagram

The class diagram above shows the main classes involved in parsing, creating, and executing commands.

How it Works

  1. User input (e.g. add company/Google role/Software Engineer deadline/17-09-2025 pay/7000) is received by CommandParser.
  2. The CommandParser:
    • Validates that the input is not empty or malformed.
    • Splits the input into a command keyword and arguments.
    • Passes them into the CommandFactory.
  3. The CommandFactory:
    • Matches the keyword to a corresponding Command subclass (e.g. AddCommand).
    • Uses the ArgumentParser to interpret argument strings.
    • Returns a fully constructed Command object.
  4. The Command object executes its logic (e.g. adds a new internship to InternshipList).
  5. Finally, the result of the execution is printed to the console via the Ui.

Sequence Diagram

The following sequence diagram illustrates how the Logic Component processes an input command:

Logic Component: Sequence Diagram

Explaining Commands with and without arguments

  1. Commands that require arguments
    • add, delete, find, list, update, username
    • These commands need extra information to execute correctly:
      • add needs company, role, deadline and pay.
      • update needs an index and fields to update.
      • find needs a search keyword.
  2. Commands that do not require arguments
    • dashboard, exit, help
    • These commands operate independently of data supplied by the user.
    • CommandFactory directly constructs the corresponding Command object (e.g. ExitCommand or DashboardCommand) without invoking ArgumentParser.

This distinction is represented in the above sequence diagram’s alt block, showing the two conditional flows:


Model Component

API: internity.core

Overview

The Model component:

Class Diagram

Model Component: Class Diagram
The class diagram above shows the main classes involved in manipulating Internship objects.

Getters and setters have been omitted from Class Diagram for clarity.

Sequence Diagram

The following sequence diagram illustrates how the Model Component processes an AddCommand:

Model Component: Sequence Diagram (Adding a new Internship)

The sequence diagram above shows how the AddCommand interacts with the InternshipList to add a new internship.

  1. The InternshipList.add() method is called with the necessary parameters (company, role, deadline, pay).
  2. InternshipList creates a new Internship object with those parameters.
  3. InternshipList calls add() to add the new internship to the static ArrayList of internships.

The following object diagram illustrates the objects after an internship has been added using the AddCommand.

Model Component - Object Diagram (After adding an internship)

After executing the AddCommand, a new Internship object is created and added to the InternshipList. The Internship object contains all relevant attributes, including company, role, pay, status, and a deadline that points to a separate Date object representing the day, month and year of the application deadline. The AddCommand maintains a reference to both the newly created Internship and its associated Date during execution. The object diagram illustrates these relationships, showing that InternshipList now contains the new Internship, and that deadline is a distinct object referenced by Internship.

The following sequence diagram illustrates how the Model Component processes an Update command:

Model Component: Sequence Diagram (Updating status of an existing internship)

The sequence diagram above shows how the UpdateCommand interacts with the InternshipList to update an existing internship.

  1. The InternshipList.update() method is called with the index of the internship to update and the new status.
  2. InternshipList retrieves the existing Internship object at that index.
  3. InternshipList calls the setStatus() method on the Internship object to update its status.

Storage Component

API: Storage.java

The Storage component:

The following class diagram shows the Storage component and its relationships:

Storage Component Class Diagram

The class diagram illustrates:


Implementation

Add feature

API: AddCommand.java

The add mechanism allows users to record new internship entries in their tracking list. This feature ensures users can maintain a comprehensive and organised list of upcoming internship opportunities, along with key details such as company, role, deadline and pay.

Implementation

The add mechanism is implemented by the AddCommand class, which extends the abstract Command class. It encapsulates the logic for validating user input, constructing an Internship object and inserting it into the internship list.

Key components involved:

How the add operation works

The following sequence illustrates how the add command is processed from user input to data persistence.

Step 1. The user launches the application and executes a command such as:

add company/Google role/Software Engineer deadline/17-09-2025 pay/7000

Step 2. The CommandParser splits the input into command word "add" and the argument string "company/Google role/Software Engineer deadline/17-09-2025 pay/7000".

Step 3. The CommandFactory delegates parsing to ArgumentParser.parseAddCommandArgs(args), which performs detailed extraction and validation of all fields.

Argument Parsing Logic

The ArgumentParser.parseAddCommandArgs() method converts a raw argument string into a valid AddCommand instance. This process ensures strict input integrity, correct field representation and data consistency.

Parsing process:

1. Input Validation

2. Splitting Fields

3. Extracting Values

4. Field Lines and Emptiness Checks

5. Data Conversion

6. Command Construction

At any failure stage: The issue is logged an appropriate InternityException is thrown to provide clear user feedback.

Command Execution Flow

Step 4. When InternityManager calls AddCommand.execute():

Step 5. After execution, InternityManager triggers InternshipList.saveToStorage(), which internally calls Storage.save() to persist the updated internship list to disk.

Example Walkthrough

Step Component Action
1 User Inputs add company/Google role/Software Engineer deadline/17-09-2025 pay/7000
2 CommandParser Separates command and arguments
3 ArgumentParser Parses and validates all four fields
4 AddCommand Creates Internship object and calls InternshipList.add()
5 Ui Displays success message

The following sequence diagram illustrates the complete add operation flow:

Add Command: Sequence Diagram

Design considerations

Aspect: Inputting parameters by prefix

Aspect: Status field

Aspect: Order of parameters

Aspect: Allowing duplicate internship entries

Rationale:

Aspect: Allowing any deadline for internship entries

Rationale:


Update feature

API: UpdateCommand.java

The update mechanism lets users modify one or more fields of an existing internship entry. It keeps the list accurate as applications evolve, without the need for users to re-enter the whole record.

Implementation

UpdateCommand extends the abstract Command class. Users specify a 1-based index in the CLI, which is converted to a 0-based index during parsing. Any subset of fields can be provided. Only non-null fields are applied.

Key components involved

How the Update Operation Works

Given below is an example usage scenario and how the update mechanism behaves at each step.

Error Handling


Example Commands

update 3 status/Interviewing             # Update only status
update 2 company/Apple role/ML Engineer  # Update company and role
update 4 deadline/15-12-2025 pay/8500    # Update deadline and pay
update 1                                 # Invalid - no fields
update status/Accepted                   # Invalid - no internship index

Design Considerations

Delete feature

API: DeleteCommand.java

The delete mechanism allows users to remove internship entries from their tracking list. This feature is essential for maintaining an up-to-date list of relevant internship applications by removing entries that are no longer needed.

Implementation

The delete mechanism is facilitated by DeleteCommand, which extends the abstract Command class. It stores an index field internally as a 0-based integer, although users interact with 1-based indices for a more natural experience.

Key components involved:

How the delete operation works

Given below is an example usage scenario and how the delete mechanism behaves at each step.

Step 1. The user launches the application and the InternshipList contains 3 internships. The user executes delete 2 to delete the 2nd internship in the list.

Step 2. The CommandParser validates the input and splits it into command word "delete" and arguments "2".

Step 3. The CommandFactory delegates to ArgumentParser.parseDeleteCommandArgs("2"), which:

Step 4. InternityManager calls DeleteCommand.execute(), which:

Step 5. After the command completes, InternityManager automatically calls InternshipList.saveToStorage(), which in turn calls Storage.save() to persist the changes to disk.

The following sequence diagram illustrates the complete delete operation flow:

Delete Command Sequence Diagram

The sequence diagram shows how the delete command flows through multiple layers:

  1. Input Layer: User input is received by InternityManager
  2. Parsing Layer: CommandParser and CommandFactory work with ArgumentParser to create the command
  3. Execution Layer: DeleteCommand interacts with InternshipList and Ui
  4. Persistence Layer: Changes are automatically saved via Storage

List feature

API: ListCommand.java

The list mechanism is implemented by the ListCommand class, which allows users to view all internships in their list.

Below is the sequence diagram for a common usage of the list feature:

List Command: Sequence Diagram

Implementation

  1. ListCommand accesses the InternshipList, which contains the ArrayList<Internship> of all stored internships.
  2. If sort/asc is specified, InternshipList.sortInternships(order) returns a new ArrayList copy sorted by deadline in ascending order. The original list is not modified.
  3. If sort/desc is specified, InternshipList.sortInternships(order) returns a new ArrayList copy sorted by deadline in descending order. The original list is not modified.
  4. If no sort option is specified, the internships are listed in the order they were added, after the last sort.
  5. The internship list is iterated through and each internship’s details are printed using Ui.printList().

Design considerations

Aspect: Index base convention

Aspect: When to retrieve internship details

Aspect: Index validation location


Find feature

API: FindCommand.java

Find Command: Sequence Diagram

The find mechanism is implemented by the FindCommand class, which allows users to search for internships based on a keyword that matches either the company name or the role of the internship.

The FindCommand class extends Command and consists of the following key components and operations:

Class and Method Breakdown

1. FindCommand Constructor

2. FindCommand.execute()

3. InternshipList.findInternship()

Example Usage Scenario

Step 1: The user launches the application and executes a find command by typing find Google.

Step 2: The CommandParser parses the input, extracting the command word find and the argument Google.

Step 3: The CommandFactory creates a FindCommand with the keyword Google.

Step 4: The FindCommand.execute() method is called, triggering the InternshipList.findInternship() method.

Step 5: findInternship() filters the internships, looking for the keyword in both the company and role fields. If any matches are found, they are displayed through the UI.

Step 6: If no matches are found, the user sees the message printed in the UI: “No internships with this company or role found.”

Internals and Key Functions

Example Interaction

Edge Cases and Considerations

Design Considerations

Aspect: Filtering criteria

Aspect: Matching behavior


Username feature

API: UsernameCommand.java

The Username feature allows the user to set a personalised username that is stored within the application’s persistent data model and displayed in future interactions.

Username Command: Sequence Diagram

Implementation

  1. The UsernameCommand is created by the CommandParser after recognising the username keyword from user input.
     username Jane Doe
    
  2. The UsernameCommand constructor validates that the argument is non-null and non-blank.
  3. When execute() is called:
    • The provided username is stored via InternshipList.setUsername(username).
    • The UI is updated through Ui.printSetUsername(username) to show the change.
  4. The command does not modify any internship data and does not terminate the application.

Design Considerations

  1. Single Responsibility: The command only handles username updates.
  2. User feedback: Ui.printSetUsername() provides clear confirmation of a successful command execution.

Dashboard feature

API: DashboardCommand.java

The Dashboard feature presents a comprehensive summary of the user’s internship tracking data, including the username, total internships, status overview and nearest deadline.

Dashboard Command: Sequence Diagram

Implementation

  1. The DashboardCommand serves as a simple trigger to call the UI layer.
       dashboard
    
  2. The DashboardUi class handles all the logic for displaying information retrieved from InternshipList.
  3. Inside DashboardUi.printDashboard(), the following occurs:
    • User display: Prints the current username using InternshipList.getUsername().
    • Internship count: Fetches and displays total internships via InternshipList.size().
    • Nearest deadline: Gets the upcoming internship with the nearest deadline using InternshipList.getNearestDeadline().
      • Case 1: If internship with future (at least today) upcoming deadline exist, displays the internship details.
      • Case 2: If no such internships exist, it displays the details of internship with the most recent past deadline and marks it as (OVERDUE!).
    • Nearest deadline count: Gets the count of internships with the same deadline as the nearest deadline internship.
    • Status overview: Aggregates internship statuses into categories (Pending, Applied, etc.) and displays a summary.
  4. If no internships exist, a meaningful fallback message is shown (e.g. “No internships found.”).

Design Considerations

  1. Separation of concerns:
    • DashboardCommand delegates all display logic to DashboardUi.
    • DashboardUI delegates all data retrieval logic to InternshipList.
  2. Read-only operation: The dashboard performs only data retrieval, ensuring no side effects.
  3. Extensibility: The DashboardUi class can easily be expanded to include additional statistics in the future.

Exit feature

API: ExitCommand.java

The ExitCommand allows the user to gracefully terminate the Internity application. Upon execution, it ensures that the user is notified and the main command loop in InternityManager is stopped.

Exit Command: Sequence Diagram

Implementation

  1. When the user enters exit, the CommandParser returns an ExitCommand instance.
  2. InternityManager calls execute() on the command, which triggers an interaction with the Ui class to display a friendly exit message before termination.
  3. Since isExit() returns true, the main loop breaks, ending the program.
  4. ExitCommand is the only Command subclass that returns true for isExit().

Help feature

API: HelpCommand.java

The HelpCommand provides a quick reference to all available commands in the Internity application. The commands are listed in an ordered, easy-to-read format to assist navigation and usage.

Help Command: Sequence Diagram

Implementation

  1. When the user enters help, the CommandParser returns a HelpCommand instance.
  2. InternityManager calls execute() on the command, which invokes the Ui.printHelp() method.
  3. Ui.printHelp() prints a formatted list detailing all commands.

Storage feature

API: Storage.java

The Storage feature provides persistent data storage for Internity, allowing users to save their internship data across application sessions. Without this feature, users would lose all their internship data upon closing the application. This is a critical feature that transforms Internity from a temporary session-based tool to a reliable long-term tracking system.

Implementation

The Storage mechanism uses a human-readable, pipe-delimited text file format that can be easily inspected and manually edited if needed. The implementation is split between the Storage class (which handles file I/O) and InternshipList (which coordinates the loading and saving operations).

Key components involved:

File format specification:

Data is stored in a single file at ./data/internships.txt with both username and internships.

Username (in line below):
<username>
<company> | <role> | <DD-MM-YYYY> | <pay> | <status>
<company> | <role> | <DD-MM-YYYY> | <pay> | <status>
...

Example:

Username (in line below):
John Doe
Google | Software Engineer | 15-12-2025 | 5000 | Pending
Meta | Data Analyst | 20-01-2026 | 4500 | Applied
Amazon | Backend Developer | 10-11-2025 | 6000 | Interview

How the storage operations work

Load Operation

The load operation occurs once during application startup, before the user sees the welcome message.

Step 1. InternityManager.start() calls InternityManager.loadData() which invokes InternshipList.loadFromStorage().

Step 2. InternshipList.loadFromStorage() calls Storage.load(), which returns an ArrayList<Internship>.

Step 3. Inside Storage.load():

Step 4. Storage.load() returns the ArrayList of successfully parsed internships.

Step 5. InternshipList.loadFromStorage() clears the static list and adds all loaded internships.

The following sequence diagram illustrates the load operation:

Storage Load Sequence Diagram

Storage Load Sequence Diagram A

Storage Load Sequence Diagram B

Storage Load Sequence Diagram C

The load sequence diagram demonstrates the robust error-handling approach: corrupted lines are skipped with warnings rather than causing the entire load operation to fail. This design choice prioritizes availability over strict consistency, ensuring users can still access their valid data even if some entries are corrupted.

Save Operation

The save operation occurs automatically after every command that modifies data (add, delete, update, username).

Step 1. After a command completes execution, InternityManager calls InternityManager.saveData(), which invokes InternshipList.saveToStorage().

Step 2. InternshipList.saveToStorage() calls Storage.save(List), passing the static ArrayList.

Step 3. Inside Storage.save():

Step 4. If any IOException occurs, wrap it in an InternityException and throw it. InternityManager catches this and displays a warning (but doesn’t crash the application).

The following sequence diagram illustrates the save operation:

Storage Save Sequence Diagram

The save sequence diagram shows the straightforward serialization process. Note that the entire file is rewritten on each save operation, which is acceptable for the target use case (up to 1000 internships) but would require optimization for larger datasets.

Atomic Save Operations

To ensure data integrity during save operations, the Storage feature employs a strategy of writing to a temporary file followed by an atomic move to replace the original file. This approach minimizes the risk of data corruption in case of application crashes or interruptions during the write process.

If the atomic move fails (e.g., due to filesystem limitations), the system falls back to a standard overwrite method while logging a warning.

Design considerations

Aspect: File format choice

Aspect: Error handling strategy

Aspect: When to save


Appendix: Requirements

Product scope

Target user profile

Value proposition

Internity provides a centralized and efficient way to manage internship applications through a command-line interface. It allows users to:

User Stories

Version As a … I want to … So that I can …
v1.0 new user add a new internship with company, role, and deadline details keep all opportunities organized in one place
v1.0 user set the status of my application (applied, interview, offer, rejected) easily see my progress with each internship
v1.0 user see a list of all my internships easily view the opportunities I’m tracking
v1.0 user remove an internship entry keep the list relevant and up to date
v2.0 user update the company, role, deadline and pay for an internship keep my application information accurate and up to date
v2.0 user see my internships sorted by deadlines prioritize applications that are due soon
v2.0 user save and load internships automatically avoid losing my progress and notes between sessions
v2.0 user find internships based on the company or role easily view my applications to specific companies or positions I’m interested in
v2.0 user set or change my username personalize my internship tracker experience
v2.0 user view a condensed dashboard to see the current status of my applications
v2.0 new user I can view an overview of the list of commands so that I can access the possible commands more conveniently

Non-Functional Requirements

  1. Should work on any mainstream OS (Windows, macOS, Linux) as long as it has Java 17 or above installed.
  2. Should be able to hold up to 1000 internship applications without a noticeable sluggishness in performance for typical usage.
  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
  4. User data should not be shared or transmitted externally.
  5. All user data shall be stored locally on the user’s device.
  6. The architecture should allow easy addition of new commands without breaking existing functionality.
  7. New commands should follow a consistent Command Pattern (execute(), isExit()).
  8. The application shall handle invalid user inputs gracefully without crashing.

Glossary

Internship
A temporary work experience offered by a company or organization that allows a student or early-career individual to gain practical skills, industry knowledge and professional exposure in a specific field. Internships may be paid or unpaid, part-time or full-time, and can occur during or after academic study.


Appendix: Instructions for manual testing

Given below are instructions to test the app manually.

Adding an internship

Test case 1: Add a valid internship

Test case 2: Add an internship with missing fields

Test case 3: Add an internship with invalid pay

Test case 4: Add an internship with an empty field


Updating an internship

Prerequisite: Have one internship added to the list.

Test case 1: Update a single field (company name)

Test case 2: Update multiple fields (company, role, and pay)

Test case 3: Invalid index

Test case 4: Missing update fields


Deleting an internship

Prerequisite: At least one internship has been added.

Test case 1: Delete an internship by index

Test case 2: Delete with invalid index (as there are fewer than 1000 internships in the list)

Test case 3: Delete with index 0 or negative index


Listing and sorting all internships

Test case 1: List all internships in the order they were added

Test case 2: List all internships sorted by deadline ascending

Test case 3: List all internships sorted by deadline descending


Finding an internship by keyword

Prerequisites: At least one internship has been added.

Test case 1: Find by company name

Test case 2: Find by role name


Changing username

Prerequisites: The application has been launched and the user is at the command prompt.

Test case 1: Changing username I

Test case 2: Changing username II

Test case 3: Invalid username input


Displaying the Internity Dashboard

Test case 1: Display dashboard with multiple internships

Test case 2: Display dashboard with no internships

Test case 3: Display dashboard after changing username

Test case 4: Dashboard reflects recent changes

Test case 5: Nearest deadline is overdue

Test case 6: Multiple internships with the same nearest deadline


Saving Data

Prerequisites:

Test case 1: Save after adding internships

Test case 2: Save after updating an internship

Test case 3: Save after deleting an internship