# Com / Github / Blaxk3 / Ui

## Overview

This module provides the graphical user interface for a **Currency Converter** desktop application built with Java Swing. It is responsible for rendering the main window, handling user input (currency selection, amount entry, and button actions), and delegating to the `CurrencyRateAPI` module for live exchange rate data and currency code lookups. A new team member should understand that this is the presentation layer — it owns no business logic beyond input validation and UI orchestration.

## Key Classes

### `UI` — Main Application Window

**Source:** [UI.java:30](src/main/java/com/github/blaxk3/ui/UI.java:30)

The `UI` class extends `javax.swing.JFrame` and serves as the top-level container for the application. It creates the window layout, wires up all Swing components, and registers the event listeners that drive the application.

#### Instance Fields

| Field | Type | Purpose |
|---|---|---|
| `label` | `JLabel` | Displays the converted amount result |
| `textField` | `JTextField` | Input field for the amount to convert |
| `comboBox1` | `JComboBox<String>` | Dropdown for selecting the source currency |
| `comboBox2` | `JComboBox<String>` | Dropdown for selecting the target currency |

#### Constructor

```java
public UI()
```

Initializes and displays the main window. The constructor:

1. Sets the application icon from `/icon/image/icon.png` on the classpath.
2. Builds the UI layout by adding the result of `panel()` to the frame.
3. Sets the title to **"Currency Converter"** with a fixed 500×500 pixel size.
4. Uses a `GridLayout(1, 2)` as the frame's layout.
5. Exits on close, centers on screen, and makes the window visible.

This is the application's entry point — instantiating a `new UI()` opens the converter window.

#### Getter/Setter Methods

These provide controlled access to the four Swing components:

- **`getComboBox1()`** / **`getComboBox2()`** — Return the source/target currency dropdowns. Used by the button listeners to read selections.
- **`getTextField()`** — Returns the amount input field. Read by the convert listener.
- **`setTextField(String msg)`** / **`setLabel(String textField)`** — Convenience mutators that set the text of the respective component. Called by the "Clear" button and the convert listener.

#### Layout Builder: `panel()`

```java
private Component panel()
```

Constructs the single vertical `JPanel` that fills the frame. It arranges two sub-panels stacked top-to-bottom (`BoxLayout.Y_AXIS`):

1. **Top row (dark gray background):** The amount `JTextField` and the source currency `JComboBox`.
2. **Bottom row (dark gray background):** The result `JLabel`, the target currency `JComboBox`, and the three action buttons ("Convert", "Swap", "Clear").

#### Component Factory Methods

Each factory method creates, configures, and returns a single component (or array of components):

| Method | Returns | Key Behavior |
|---|---|---|
| `comboBox1()` | `JPanel` | Creates a combo box, triggers async currency code fetch via `CurrencyCode`, returns a panel wrapping it |
| `comboBox2()` | `JPanel` | Identical to `comboBox1()` but for the target currency |
| `button()` | `JPanel[]` | Creates three `JButton` instances (200×35 px each), attaches all three listeners, returns an array of buttons |
| `textField()` | `JPanel` | Creates a large 24pt bold text field, installs `NumericFilter` for input validation |
| `label()` | `JPanel` | Creates a white-background label for displaying conversion results |

The factory pattern keeps component creation encapsulated and makes each panel independent.

#### Button Listeners

The `button()` method attaches three `ActionListener`s to its buttons:

**Convert button** (`"Convert"`):
1. Reads the text field and validates it is non-empty and not just `"."`.
2. If empty, shows a `JOptionPane` error dialog: *"Please enter the amount you need"*.
3. Otherwise, calls `CurrencyRateAPI.convert(sourceCurrency, targetCurrency, amount)` and formats the result with `DecimalFormat("#,###.###")`, then displays it in the result `label`.
4. Catches `MalformedURLException` and `URISyntaxException`, wrapping them in a `RuntimeException`.

**Swap button** (`"Swap"`):
1. Reads the currently selected item from `comboBox1` and stores it.
2. Sets `comboBox1`'s selection to `comboBox2`'s selection.
3. Sets `comboBox2`'s selection to the stored value.
4. This effectively reverses the conversion direction.

**Clear button** (`"Clear"`):
1. Clears the text field.
2. Clears the result label.

#### Nested Class: `NumericFilter`

**Source:** [UI.java:167](src/main/java/com/github/blaxk3/ui/UI.java:167)

`NumericFilter` extends `javax.swing.text.DocumentFilter` and restricts the amount text field to valid numeric input. It is installed on the `JTextField`'s document via `setDocumentFilter()`.

It intercepts two document operations:

- **`insertString()`** — Called when the user types characters. It reconstructs the document's content with the new characters inserted and checks validity.
- **`replace()`** — Called when the user pastes or backspaces. It reconstructs the content with the replacement applied and checks validity.

**Validation logic** (`isValid(String text)`):

1. Empty string is always valid (allows the field to be cleared).
2. Counts decimal points — at most one `.` is allowed.
3. Every other character must be a digit (`Character.isDigit()`).

This prevents users from entering negative numbers, letters, or multiple decimal points.

#### Nested Class: `CurrencyCode`

**Source:** [UI.java:214](src/main/java/com/github/blaxk3/ui/UI.java:214)

`CurrencyCode` extends `SwingWorker<String[], Void>` and fetches the list of available currency codes from the exchange rate API on a background thread. This avoids blocking the Event Dispatch Thread (EDT) during startup.

- **`doInBackground()`** — Calls `CurrencyRateAPI.getCurrencyCode()` which makes an HTTP GET request to the exchange rate service.
- **`done()`** — Runs on the EDT after background work completes. If successful, it sorts the currency codes alphabetically and adds each to the associated `JComboBox`. If the fetch fails, it logs the error via SLF4J and silently leaves the combo box empty.

This class is instantiated in both `comboBox1()` and `comboBox2()`, meaning each dropdown independently fetches the currency list in parallel.

## How It Works

### Typical User Flow

```mermaid
flowchart TD
    UI["UI extends JFrame<br/>Main GUI window"] --> cb1["JComboBox<br/>Source currency"]
    UI --> cb2["JComboBox<br/>Target currency"]
    UI --> tf["JTextField<br/>Amount input"]
    UI --> lbl["JLabel<br/>Result display"]
    UI --> btn1["JButton<br/>Convert"]
    UI --> btn2["JButton<br/>Swap"]
    UI --> btn3["JButton<br/>Clear"]
    btn1 --> api["CurrencyRateAPI<br/>Exchange rate service"]
    cc["CurrencyCode SwingWorker"] --> api
    cc --> cb1
    cc --> cb2
    nf["NumericFilter DocumentFilter"] --> tf
</flowchart>
```

### Sequence of Events

**1. Application startup**
- `new UI()` creates the frame and calls `panel()`.
- The panel factory creates the text field with a `NumericFilter` and both combo boxes.
- Each combo box spawns a `CurrencyCode` `SwingWorker` that fetches currency codes from the API in the background.
- When the workers complete, `done()` populates both dropdowns with sorted currency codes.

**2. User enters an amount and selects currencies**
- The `NumericFilter` validates each keystroke, only allowing digits and at most one decimal point.
- The user selects a source currency from the first dropdown and a target from the second.

**3. User clicks "Convert"**
- The listener reads the amount from the text field and checks it is non-empty.
- If the field is empty, an error dialog appears.
- Otherwise, it calls `CurrencyRateAPI.convert(source, target, amount)` to fetch the live exchange rate and compute the converted amount.
- The result is formatted with `DecimalFormat("#,###.###")` (grouping separator, up to 3 decimal places) and displayed in the result label.

**4. User clicks "Swap"**
- The selected items in the two combo boxes are exchanged, reversing the conversion direction. The converted amount is not recalculated automatically — the user must click "Convert" again.

**5. User clicks "Clear"**
- Both the input field and result label are cleared to empty strings.

## Dependencies and Integration

| Dependency | Direction | Purpose |
|---|---|---|
| `com.github.blaxk3.api.CurrencyRateAPI` | **Uses** | Provides exchange rate lookups (`convert()`) and currency code lists (`getCurrencyCode()`) via an external HTTP API (ExchangeRate API v6) |
| SLF4J | **Uses** | Logging framework for error reporting in the `CurrencyCode` worker |
| Java Swing | **Uses** | All UI components (`JFrame`, `JPanel`, `JComboBox`, `JTextField`, `JButton`, `JLabel`) |
| Java AWT | **Uses** | Layout managers, `Color`, `Dimension`, `Font` |
| `javax.swing.SwingWorker` | **Uses** | Background fetching of currency codes to avoid blocking the EDT |
| `javax.swing.text.DocumentFilter` | **Uses** | Custom input validation on the text field |

The UI has a single external dependency: the `CurrencyRateAPI` module. The API reads an `API_KEY` from `config.properties` on the classpath and communicates with the Exchangerate API v6 REST endpoint over HTTP.

## Notes for Developers

### Architecture

This is a straightforward single-class Swing application. All components are created programmatically (no GUI builder, no FXML). The `panel()` method and its child factory methods follow a composition pattern where each method returns a self-contained panel.

### Threading

- `CurrencyCode` uses `SwingWorker` to fetch currency codes off the EDT — this is correct. The `done()` method runs on the EDT and updates Swing components directly.
- The "Convert" button's action listener calls `CurrencyRateAPI.convert()` on the EDT. This is a blocking HTTP call and should ideally also be offloaded to a background thread to prevent UI freezing during network latency.

### Input Validation

The `NumericFilter` is a simple but effective validator. It does not support negative numbers or scientific notation — which is fine for a currency converter that deals with positive amounts only.

### Extending the UI

To add functionality, the natural extension points are:

- **`panel()`** — Add new rows or sub-panels to the layout.
- **`button()`** — Add more `JButton` instances and register listeners.
- **`NumericFilter`** — Extend `isValid()` to support additional character types.

### Limitations

- The UI is not resizeable (`setResizable(false)`) and has a fixed 500×500 size.
- There is no error handling for `CurrencyRateAPI` network failures in the "Convert" listener beyond throwing a `RuntimeException`. The `CurrencyCode` worker does log errors, but the UI gives no visual feedback to the user that the dropdowns are empty.
- The "Swap" button swaps dropdown selections but does not clear or recalculate the result label.

### Code Conventions

- All UI factories are `private` and return `Component` types, keeping the internal component structure encapsulated.
- `NumericFilter` and `CurrencyCode` are static nested classes, which is appropriate since they do not need access to `UI` instance fields.
- The class uses `java.util.Objects.requireNonNull()` defensively in button listeners, guarding against `NullPointerException` when a combo box has no selection.
