# Com / Github / Blaxk3 / Ui

## Overview

This module is the graphical user interface layer for a **Currency Converter** desktop application built with Java Swing. It provides a window with input fields for entering an amount and selecting source/target currencies, fetches live exchange rate data in the background, and displays the converted result. It exists to give end users a simple point-and-click interface to perform real-time currency conversions.

---

## Key Classes and Interfaces

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

The main application window, extending `javax.swing.JFrame`. This class assembles the entire UI layout, wires up event listeners, and orchestrates the background fetch of currency codes.

#### Fields

| Field | Type | Description |
|-------|------|-------------|
| `label` | `JLabel` | Displays the conversion result |
| `textField` | `JTextField` | Where the user enters the amount to convert |
| `comboBox1` | `JComboBox<String>` | Source currency dropdown |
| `comboBox2` | `JComboBox<String>` | Target currency dropdown |

#### Constructor

**`UI()`** (line 58)

Initializes and shows the application window:

- Loads a custom icon from `/icon/image/icon.png` on the classpath.
- Adds the main panel built by `panel()` as the content.
- Sets the title to "Currency Converter", window size to 500x500, and a centered layout.
- Makes the window non-resizable and centers it on screen.
- Calls `setVisible(true)` to display the window.

#### Layout Methods

**`panel()`** (line 70) — Returns the root `JPanel` that fills the window.

The layout is a two-column grid (the window itself uses `GridLayout(1, 2)`), and within the left column the panel stacks two sub-panels vertically using `BoxLayout.Y_AXIS`:

1. **Row 1** (dark gray background, center-aligned): the amount text field and the source currency combobox.
2. **Row 2** (dark gray background, center-aligned): the result label, the target currency combobox, and three action buttons ("Convert", "Swap", "Clear").

**`textField()`** (line 149) — Creates the amount input field.

- Font: Arial Bold, size 24.
- Size: 300 x 100 pixels.
- Attaches a `NumericFilter` document filter to restrict input to digits and at most one decimal point.

**`label()`** (line 158) — Creates the result display label.

- Font: Arial Bold, size 24.
- Size: 300 x 100 pixels.
- Opaque background set to white for contrast against the dark gray panel.

**`comboBox1()`** (line 95) and **`comboBox2()`** (line 103) — Create source and target currency dropdowns.

- Both are 300 x 30 pixels.
- Each triggers a background task (`CurrencyCode`) to populate the dropdown with available currency codes from the API.

**`button()`** (line 111) — Creates three action buttons, each 200 x 35 pixels.

| Button | Action |
|--------|--------|
| **Convert** | Reads the amount from the text field and both selected currencies from the comboboxes. Calls `CurrencyRateAPI.convert()` to fetch the conversion rate and displays the formatted result in the label. If the text field is empty or contains only a period, shows an error dialog. |
| **Swap** | Swaps the selected items between combobox1 and combobox2, effectively reversing the conversion direction. |
| **Clear** | Clears both the text field and the result label. |

The Convert button formats the output using `DecimalFormat("#,###.###")` — grouping digits with commas and showing up to 3 decimal places.

#### Accessors

| Method | Return | Description |
|--------|--------|-------------|
| `getComboBox1()` (line 38) | `JComboBox<String>` | Returns the source currency combo box. |
| `getComboBox2()` (line 42) | `JComboBox<String>` | Returns the target currency combo box. |
| `getTextField()` (line 46) | `JTextField` | Returns the amount input field. |
| `setTextField(String)` (line 50) | `void` | Sets the text of the input field. |
| `setLabel(String)` (line 54) | `void` | Sets the text of the result label. |

---

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

A `javax.swing.text.DocumentFilter` that restricts text input in the amount field to valid numeric strings. It is applied to the text field's document at construction time via `setDocumentFilter()`.

#### Methods

**`insertString(...)`** (line 169)

Called when the user types or pastes characters. It:
1. Reconstructs the document content with the new string inserted at the cursor position.
2. Validates the resulting string using `isValid()`.
3. If valid, delegates to `super.insertString()` to actually perform the insertion.

**`replace(...)`** (line 182)

Called when characters are deleted or replaced (e.g., backspace, selection overwrite). It:
1. Reconstructs the document content with the replacement applied.
2. Validates the resulting string using `isValid()`.
3. If valid, delegates to `super.replace()`.

**`isValid(String)`** (line 195)

Validates that the string contains only digits and at most one decimal point:

- Empty strings are allowed (this lets the user backspace to nothing).
- Counts decimal points — if more than one is found, the string is rejected.
- Any non-digit, non-dot character causes immediate rejection.

This prevents users from entering invalid values like "12.3.4" or "abc", though it does not prevent negative numbers or leading zeros.

---

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

A `SwingWorker<String[], Void>` that fetches available currency codes from the [CurrencyRateAPI](src/main/java/com/github/blaxk3/api/CurrencyRateAPI.java) in the background and populates a combo box with the results. It is instantiated and executed separately for `comboBox1` and `comboBox2` (lines 98 and 106).

#### Methods

**`CurrencyCode(JComboBox<String>)`** (line 217)

Constructor. Stores a reference to the target combo box so the results can be written back to the Swing EDT.

**`doInBackground()`** (line 221)

Runs on a background thread. Calls `CurrencyRateAPI.getCurrencyCode()` and returns the string array of currency codes.

**`done()`** (line 227)

Runs on the Swing Event Dispatch Thread (EDT) after `doInBackground()` completes. It:
1. Calls `get()` to retrieve the result array (blocking if necessary).
2. If the result is non-null, streams it through `Arrays.stream().sorted().forEach()`, adding each currency code to the combo box in alphabetical order.
3. Catches `InterruptedException` and `ExecutionException`, logging the error via SLF4J rather than crashing the UI.

This design ensures that the potentially slow network call does not block the UI thread.

---

## How It Works

### Typical user flow

```mermaid
flowchart TD
    A["UI Constructor"] --> B["Create panel layout"]
    B --> C["Create text field"]
    C --> D["Attach NumericFilter"]
    B --> E["Create comboBox1"]
    B --> F["Create comboBox2"]
    E --> G["Launch CurrencyCode background task"]
    F --> H["Launch CurrencyCode background task"]
    G --> I["Fetch currency codes from API"]
    H --> I
    I --> J["Populate comboboxes sorted"]
    B --> K["Create Convert, Swap, Clear buttons"]
    L["User enters amount"] --> M["NumericFilter validates input"]
    M --> N["User selects currencies"]
    N --> O["User clicks Convert"]
    O --> P["Call CurrencyRateAPI.convert"]
    P --> Q["Display result in label"]
```

### Step by step

1. **Window initialization**: The `UI()` constructor builds the entire layout tree and shows the window. During layout creation, two `CurrencyCode` background tasks are launched in parallel — one for each combobox.

2. **Currency code population**: Each `CurrencyCode` worker makes a network request to the exchange rate API via `CurrencyRateAPI.getCurrencyCode()`. When the response arrives, the sorted codes are added to the combo boxes on the EDT.

3. **User input**: The user types an amount. The `NumericFilter` intercepts every insert and replace operation, rejecting any input that is not a valid non-negative number (digits and an optional single period).

4. **Conversion**: When the user clicks **Convert**:
   - The text field is checked for emptiness.
   - The selected items from both combo boxes are extracted.
   - `CurrencyRateAPI.convert()` is called with the source currency, target currency, and amount as a `BigDecimal`.
   - The result is formatted with `DecimalFormat("#,###.###")` and displayed in the label.
   - Network errors (`MalformedURLException`, `URISyntaxException`) are rethrown as `RuntimeException`.

5. **Swap**: The **Swap** button exchanges the source and target currency selections, letting the user quickly reverse the conversion direction without changing their amount.

6. **Clear**: The **Clear** button resets both the input field and the result label.

---

## Dependencies and Integration

### External dependencies

| Dependency | Used For |
|------------|----------|
| `com.github.blaxk3.api.CurrencyRateAPI` | Fetching exchange rates and currency code lists from the Exchangerate API |
| `javax.swing.*` | All UI components (JFrame, JPanel, JButton, JComboBox, JTextField, JLabel) |
| `org.slf4j` | Logging errors during background task execution |
| `java.awt.*` | Layout managers, dimensions, colors, fonts |
| `java.math.BigDecimal` | Precise currency amount arithmetic |
| `javax.swing.SwingWorker` | Background execution of API calls |
| `javax.swing.text.DocumentFilter` | Input validation |

### API integration

The module depends on the [CurrencyRateAPI](src/main/java/com/github/blaxk3/api/CurrencyRateAPI.java) class from the `com.github.blaxk3.api` package. This API class:

- Reads an API key from `config.properties` on the classpath.
- Constructs URLs against `https://v6.exchangerate-api.com/v6/`.
- Provides `getCurrencyCode()` to return a `String[]` of supported currency codes.
- Provides `convert(source, target, amount)` to return the converted amount.

Errors from the API call during conversion are not caught locally — they propagate as a `RuntimeException`.

---

## Notes for Developers

### Input validation quirks

`NumericFilter.isValid()` allows leading zeros (e.g., "007") and does not allow negative numbers. The only non-digit character it permits is the period (`.`), and only one instance. It does not enforce that the period is surrounded by digits on both sides — a string of "." alone passes validation. The Convert button guards against this by also checking that the text is not just "." before proceeding.

### Background task safety

`CurrencyCode` is designed so that `doInBackground()` performs the network call while `done()` updates the Swing component on the EDT. This is correct usage of `SwingWorker`. However, if both combobox tasks are running simultaneously and a user clicks Convert before either has finished, the comboboxes may still be empty, which will throw a `NullPointerException` on `getSelectedItem()`. The `Objects.requireNonNull()` guards will surface this as a runtime exception.

### Error handling asymmetry

The Convert button's `ActionListener` catches `MalformedURLException` and `URISyntaxException` from the API call but rethrows them as `RuntimeException`. In contrast, the `CurrencyCode.done()` method catches and logs `InterruptedException` and `ExecutionException` from the background task. This means API connectivity failures during a manual convert will crash the application, while failures during the initial currency code fetch are silently logged.

### No auto-refresh

Currency codes are only fetched once — when the combo boxes are created. There is no mechanism to refresh the available currencies while the application is running. If the API adds or removes supported currencies, the user would need to restart the application.

### Extending the UI

To add new features:

- **New buttons**: Add more `JButton` instances to the array in `button()` and attach listeners.
- **New input types**: Create subclasses of `NumericFilter` with different `isValid()` logic and attach them via `setDocumentFilter()`.
- **New data sources**: Create another `SwingWorker` subclass similar to `CurrencyCode` and execute it to populate components on the EDT.
