# (DD01) UI — Class Detailed Design [91 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `com.github.blaxk3.ui.UI` |
| Layer | UI / View (Swing MVC View) |
| Module | `com.github.blaxk3.ui` |

## Class Overview

`UI` is the main entry point and top-level view class of the Currency Converter application. It extends `javax.swing.JFrame` and constructs the entire graphical user interface — a pair of panels containing text fields, combo boxes for currency selection, a results label, and action buttons (Convert, Swap, Clear). The class delegates currency code loading to the nested `CurrencyCode` SwingWorker (which calls `CurrencyRateAPI`) and performs real-time currency conversion in the Convert button listener. It also embeds a `NumericFilter` `DocumentFilter` to restrict the input text field to numeric characters and a single decimal point.

---

## Method: button()

### 1. Role
Creates the three action buttons (Convert, Swap, Clear) with their layout properties and event listeners. This is the core interaction method of the UI — wiring user clicks to the business logic of currency conversion, currency swapping, and clearing inputs.

### 2. Processing Pattern

```
flowchart TD
    A["button() start"] --> B["Create JButton array - Convert/Swap/Clear"]
    B --> C["Set 200x35 preferred size on each button"]
    C --> D["Register Convert ActionListener"]
    D --> E["Register Swap ActionListener"]
    E --> F["Register Clear ActionListener"]
    F --> G["Return JButton[]"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| *(none)* | — | — | — | No parameters; factory method creating UI components |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | API Call | `CurrencyRateAPI.convert()` | HTTP GET to ExchangeRate-API for currency conversion |
| 2 | API Call | `CurrencyRateAPI` constructor | Creates `new CurrencyRateAPI()` to access `convert()` |
| 3 | UI Display | `setLabel()` | Formats and displays conversion result |
| 4 | UI Display | `JOptionPane.showMessageDialog()` | Shows error dialog when input is invalid |
| 5 | UI Read | `getTextField().getText()` | Reads amount input |
| 6 | UI Read | `getComboBox1().getSelectedItem()` | Reads source currency |
| 7 | UI Read | `getComboBox2().getSelectedItem()` | Reads target currency |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `panel()` | `button()` | Receives `JButton[]`, adds elements to `panelFramePanel2` |
| `panel()` | `button()[0]` | Add Convert button to panel |
| `panel()` | `button()[1]` | Add Swap button to panel |
| `panel()` | `button()[2]` | Add Clear button to panel |
| `panel()` | `button()` | Loop index [0], [1], [2] — adds all three buttons |

### 6. Per-Branch Detail Blocks

#### 6.1 Convert Button Listener (button[0])

```
flowchart TD
    A["Convert button clicked"] --> B["Check textField is not empty AND not '.'"]
    B --> C["Empty or dot - show error dialog"]
    B --> D["Valid input - parse amount as Double"]
    D --> E["Call rate.convert(from, to, amount)"]
    E --> F["Format with DecimalFormat #,###.###"]
    F --> G["Set formatted string as label text"]
```

- **Validation branch**: Checks `!getTextField().getText().isEmpty() && !getTextField().getText().equals(".")`. If either condition is true (field is empty or only a dot), a `JOptionPane` error dialog is shown with message "Please enter the amount you need".
- **Conversion branch**: If valid, parses the text field as `Double`, calls `rate.convert(foreignCurrency1, foreignCurrency2, BigDecimal.valueOf(amount))` which performs an HTTP GET to the ExchangeRate-API, then formats the result using `DecimalFormat("#,###.###")` (comma thousands separator, up to 3 decimal places).
- **Exception handling**: Catches `MalformedURLException` and `URISyntaxException`, re-throws as `RuntimeException`. The `Objects.requireNonNull()` guards protect against null combo box selections.

#### 6.2 Swap Button Listener (button[1])

- Reads source currency from `comboBox1.getSelectedItem()`.
- Sets `comboBox1`'s selection to the current `comboBox2` item.
- Sets `comboBox2`'s selection to the original `comboBox1` item.
- Effectively swaps the two currency selections. Uses `Objects.requireNonNull()` to guard against null selections.

#### 6.3 Clear Button Listener (button[2])

- Calls `setTextField("")` — clears the input amount field.
- Calls `setLabel("")` — clears the conversion result label.

---

## Method: panel()

### 1. Role
Constructs the two-row layout of the main frame: the top row holds the input amount text field and the source currency combo box; the bottom row holds the result label, target currency combo box, and the three action buttons.

### 2. Processing Pattern

```
flowchart TD
    A["panel() start"] --> B["Create main JPanel"]
    B --> C["Set BoxLayout Y_AXIS"]
    C --> D["Create panelFramePanel1"]
    D --> E["Set DARK_GRAY background, FlowLayout CENTER"]
    E --> F["Add textField() and comboBox1()"]
    F --> G["Create panelFramePanel2"]
    G --> H["Set FlowLayout CENTER"]
    H --> I["Add label(), comboBox2(), button[]"]
    I --> J["Set DARK_GRAY background"]
    J --> K["Add both sub-panels to main framePanel"]
    K --> L["Return framePanel"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| *(none)* | — | — | — | Factory method building the panel hierarchy |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | Component Create | `new JPanel()` | Creates main panel and two sub-panels |
| 2 | Method Call | `textField()` | Provides input text field |
| 3 | Method Call | `comboBox1()` | Provides source currency selector |
| 4 | Method Call | `label()` | Provides result display label |
| 5 | Method Call | `comboBox2()` | Provides target currency selector |
| 6 | Method Call | `button()` | Provides Convert, Swap, Clear buttons |
| 7 | Layout Setup | `BoxLayout`, `FlowLayout` | Arranges components in 2-row layout |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `UI()` (constructor) | `panel()` | Called as `add(panel())` — the panel becomes the frame's content |

### 6. Per-Branch Detail Blocks

#### 6.1 Main Panel Setup

- Creates a `JPanel` named `framePanel`.
- Sets layout to `javax.swing.BoxLayout(framePanel, Y_AXIS)` — vertical stacking of sub-panels.

#### 6.2 Top Row Panel (panelFramePanel1)

- Creates `JPanel panelFramePanel1`.
- Background: `Color.DARK_GRAY`.
- Layout: `FlowLayout(FlowLayout.CENTER)`.
- Contains: `textField()` (left) and `comboBox1()` (right).

#### 6.3 Bottom Row Panel (panelFramePanel2)

- Creates `JPanel panelFramePanel2`.
- Layout: `FlowLayout(FlowLayout.CENTER)`.
- Background: `Color.DARK_GRAY` (set after adding components — order does not matter for `FlowLayout`).
- Contains: `label()`, `comboBox2()`, `button()[0]` (Convert), `button()[1]` (Swap), `button()[2]` (Clear).

#### 6.4 Assembly

- Adds `panelFramePanel1` and `panelFramePanel2` to `framePanel` in order.
- Returns `framePanel`.

---

## Method: label()

### 1. Role
Creates and configures the result display `JLabel` that shows the converted currency amount.

### 2. Processing Pattern

```
flowchart TD
    A["label() start"] --> B["Create JLabel"]
    B --> C["Set Arial Bold 24pt font"]
    C --> D["Set preferred size 300x100"]
    D --> E["Set opaque true"]
    E --> F["Set WHITE background"]
    F --> G["Return label"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| *(none)* | — | — | — | Factory method; stores result in instance field `label` |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | Component Create | `new JLabel()` | Creates an empty label |
| 2 | Field Assignment | `this.label` | Stores reference as instance field |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `panel()` | `label()` | Adds label to `panelFramePanel2` |

### 6. Per-Branch Detail Blocks

#### 6.1 Label Configuration

- `new JLabel()` — creates an empty label with no text.
- `setFont(new Font("Arial", Font.BOLD, 24))` — large bold font for readability.
- `setPreferredSize(new Dimension(300, 100))` — 300x100 pixels.
- `setOpaque(true)` — enables background painting.
- `setBackground(Color.WHITE)` — white background to contrast with dark gray panels.
- Returns the label component.

---

## Method: textField()

### 1. Role
Creates the input text field where users type the amount to convert. Applies a custom `NumericFilter` to enforce numeric-only input.

### 2. Processing Pattern

```
flowchart TD
    A["textField() start"] --> B["Create JTextField"]
    B --> C["Set Arial Bold 24pt font"]
    C --> D["Set preferred size 300x100"]
    D --> E["Get PlainDocument from JTextField"]
    E --> F["Set NumericFilter document filter"]
    F --> G["Return textField"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| *(none)* | — | — | — | Factory method; stores in instance field `textField` |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | Component Create | `new JTextField()` | Creates empty text field |
| 2 | Font Create | `new Font("Arial", Font.BOLD, 24)` | Styled font |
| 3 | Class Instantiate | `new NumericFilter()` | Input validation filter |
| 4 | Field Assignment | `this.textField` | Stores reference |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `panel()` | `textField()` | Adds text field to `panelFramePanel1` |

### 6. Per-Branch Detail Blocks

#### 6.1 TextField Configuration

- `new JTextField()` — creates an empty text field.
- `setFont(new Font("Arial", Font.BOLD, 24))` — large bold font.
- `setPreferredSize(new Dimension(300, 100))` — 300x100 pixels.
- `((javax.swing.text.PlainDocument) textField.getDocument()).setDocumentFilter(new NumericFilter())` — casts the text field's document to `PlainDocument` and attaches the `NumericFilter` to validate input character-by-character on insert and replace operations.
- Returns the text field.

---

## Method: comboBox1()

### 1. Role
Creates the source currency combo box and initiates an asynchronous background fetch of available currency codes from the ExchangeRate-API.

### 2. Processing Pattern

```
flowchart TD
    A["comboBox1() start"] --> B["Create JComboBox"]
    B --> C["Set preferred size 300x30"]
    C --> D["Instantiate CurrencyCode SwingWorker"]
    D --> E["Execute background fetch"]
    E --> F["Return comboBox1"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| *(none)* | — | — | — | Factory method; stores in instance field `comboBox1` |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | Component Create | `new JComboBox<String>()` | Creates empty combo box |
| 2 | SwingWorker | `new CurrencyCode(comboBox1).execute()` | Background API fetch |
| 3 | Field Assignment | `this.comboBox1` | Stores reference |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `panel()` | `comboBox1()` | Adds combo box to `panelFramePanel1` |

### 6. Per-Branch Detail Blocks

#### 6.1 Combo Box Creation

- `new JComboBox<String>()` — creates an empty combo box.
- `setPreferredSize(new Dimension(300, 30))` — 300x30 pixels.
- `new CurrencyCode(comboBox1).execute()` — creates and executes a `SwingWorker<String[], Void>` that:
  - Calls `CurrencyRateAPI.getCurrencyCode()` in `doInBackground()` (background thread).
  - On completion, in `done()`, sorts the currency codes and adds each item to the combo box.
- Returns `comboBox1`.

---

## Method: comboBox2()

### 1. Role
Creates the target currency combo box and initiates the same asynchronous fetch of currency codes as `comboBox1()`.

### 2. Processing Pattern

```
flowchart TD
    A["comboBox2() start"] --> B["Create JComboBox"]
    B --> C["Set preferred size 300x30"]
    C --> D["Instantiate CurrencyCode SwingWorker"]
    D --> E["Execute background fetch"]
    E --> F["Return comboBox2"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| *(none)* | — | — | — | Factory method; stores in instance field `comboBox2` |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | Component Create | `new JComboBox<String>()` | Creates empty combo box |
| 2 | SwingWorker | `new CurrencyCode(comboBox2).execute()` | Background API fetch |
| 3 | Field Assignment | `this.comboBox2` | Stores reference |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `panel()` | `comboBox2()` | Adds combo box to `panelFramePanel2` |

### 6. Per-Branch Detail Blocks

#### 6.1 Combo Box Creation

- `new JComboBox<String>()` — creates an empty combo box.
- `setPreferredSize(new Dimension(300, 30))` — 300x30 pixels.
- `new CurrencyCode(comboBox2).execute()` — creates and executes a `SwingWorker<String[], Void>` with the `CurrencyCode` inner class:
  - `doInBackground()` calls `new CurrencyRateAPI().getCurrencyCode()` to fetch all available currency codes via HTTP GET.
  - `done()` retrieves the result via `get()`, sorts the codes with `Arrays.stream(currencyCodes).sorted()`, and adds each as an item to the combo box.
  - Catches `InterruptedException` and `ExecutionException`, logs error via SLF4J.
- Returns `comboBox2`.

---

## (Optional) Inner Class: NumericFilter

### 1. Role
A `DocumentFilter` subclass that validates user input in the amount text field, allowing only digits and at most one decimal point.

### 2. Processing Pattern

```
flowchart TD
    A["NumericFilter.isValid(text)"] --> B["Text empty?"]
    B --> C["Yes - return true (allow)"]
    B --> D["No - scan characters"]
    D --> E["Count decimal points"]
    E --> F["Found second '.'?"]
    F --> G["Yes - reject (return false)"]
    F --> H["Found non-digit char?"]
    H --> I["Yes - reject (return false)"]
    H --> J["All valid - return true"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Notes |
|-----------|------|-----------|-------|
| `text` | `String` | Input | Full document text (before insert/after replace) |

### 4. Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | Validation | `Character.isDigit(ch)` | Checks each character is a digit |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `insertString()` | `isValid()` | Validates after simulated insert |
| `replace()` | `isValid()` | Validates after simulated replace |

### 6. Per-Branch Detail Blocks

#### 6.1 insertString()

- Receives text to insert via `FilterBypass`.
- Reads current document content, builds a new string with the text inserted at offset.
- Calls `isValid()` — if valid, calls `super.insertString()` to apply the change.

#### 6.2 replace()

- Receives replacement text via `FilterBypass`.
- Reads current document content, replaces a range with the new text.
- Calls `isValid()` — if valid, calls `super.replace()` to apply the change.

#### 6.3 isValid()

- Returns `true` for empty strings.
- Scans all characters: tracks decimal point count (max 1 allowed), rejects any non-digit character.

---

## (Optional) Inner Class: CurrencyCode

### 1. Role
A `SwingWorker<String[], Void>` that asynchronously fetches all available currency codes from the ExchangeRate-API and populates a `JComboBox` on the Event Dispatch Thread (EDT).

### 2. Processing Pattern

```
flowchart TD
    A["CurrencyCode.execute()"] --> B["Background: doInBackground()"]
    B --> C["Create CurrencyRateAPI"]
    C --> D["Call getCurrencyCode()"]
    D --> E["HTTP GET to API /latest/USD"]
    E --> F["Parse JSON conversion_rates keys"]
    F --> G["Return String[]"]
    G --> H["EDT: done()"]
    H --> I["get() result"]
    I --> J["Sort and add items to comboBox"]
```

### 3. Parameter Analysis

| Parameter | Type | Direction | Nullable | Notes |
|-----------|------|-----------|----------|-------|
| `comboBox` | `JComboBox<String>` | Input | No | Combo box to populate |

### 4. CRUD Operations / Called Services

| # | Operation | Target | Details |
|---|-----------|--------|---------|
| 1 | API Call | `CurrencyRateAPI.getCurrencyCode()` | HTTP GET to fetch currency codes |
| 2 | UI Update | `comboBox.addItem()` | Adds sorted codes to combo box |
| 3 | Logging | `logger.error()` | Catches and logs fetch errors |

### 5. Dependency Trace

| Caller | Called Method / Field | How |
|--------|-----------------------|-----|
| `comboBox1()` | `new CurrencyCode(comboBox1).execute()` | Fetches codes for source currency |
| `comboBox2()` | `new CurrencyCode(comboBox2).execute()` | Fetches codes for target currency |

### 6. Per-Branch Detail Blocks

#### 6.1 doInBackground()

- Runs on a background thread (not EDT).
- Creates `new CurrencyRateAPI()` and calls `getCurrencyCode()`.
- `getCurrencyCode()` internally: builds the API URL using the API key from `config.properties`, makes an HTTP GET to `/latest/USD`, parses the JSON response, extracts `conversion_rates` keys, and returns them as `String[]`.

#### 6.2 done()

- Runs on the EDT (SwingWorker callback).
- Calls `get()` to retrieve the result from `doInBackground()`.
- If result is non-null, sorts the codes and adds each to the combo box.
- Catches `InterruptedException | ExecutionException`, logs error with SLF4J.

---

## Cross-Method Architecture

```
flowchart TD
    A["CurrencyConverter.main()"] --> B["SwingUtilities.invokeLater"]
    B --> C["UI constructor"]
    C --> D["setIconImage()"]
    C --> E["add(panel())"]
    E --> F["panel()"]
    F --> G["textField()"]
    F --> H["comboBox1()"]
    F --> I["label()"]
    F --> J["comboBox2()"]
    F --> K["button()"]
    K --> L["Convert listener"]
    L --> M["CurrencyRateAPI.convert()"]
    H --> N["CurrencyCode SwingWorker"]
    N --> O["CurrencyRateAPI.getCurrencyCode()"]
    J --> P["CurrencyCode SwingWorker"]
    P --> O
```

**Data Flow Summary:**
1. `CurrencyConverter.main()` launches the UI via `SwingUtilities.invokeLater(UI::new)`.
2. The `UI` constructor sets the window icon, title, size, layout, and calls `panel()` to build the component hierarchy.
3. `panel()` composes two sub-panels: the input row (`textField` + `comboBox1`) and the action row (`label` + `comboBox2` + `button[]`).
4. `comboBox1()` and `comboBox2()` trigger `CurrencyCode` SwingWorkers that asynchronously call `CurrencyRateAPI.getCurrencyCode()` to fetch and populate currency codes.
5. The Convert button listener reads the text field, both combo boxes, and calls `CurrencyRateAPI.convert()` to perform real-time conversion, formatting the result with `DecimalFormat` before displaying it in the label.
6. The Swap button exchanges the selections of the two combo boxes.
7. The Clear button resets both the text field and label to empty strings.
8. Input validation is enforced at the document level by `NumericFilter`, ensuring only numeric characters and a single decimal point are accepted.
