# Com / Github / Blaxk3

## Overview

This package is the root of a **Currency Converter** desktop application built with Java Swing. It provides a graphical front-end for performing real-time foreign exchange conversions against the [exchangerate-api.com](https://www.exchangerate-api.com/) REST service. The module consists of three sub-packages that together form a complete, standalone desktop app — a launcher, a GUI layer, and an HTTP client facade — without any framework dependencies (no Spring, CDI, or build scaffolding beyond a minimal `config.properties` file).

## Sub-module Guide

| Sub-module | Path | Responsibility |
|------------|------|----------------|
| `converter` | `com.github.blaxk3.converter` | Application entry point; bootstraps Swing on the Event Dispatch Thread (EDT) |
| `ui` | `com.github.blaxk3.ui` | GUI layer; window layout, input validation, background currency-code fetching, and conversion orchestration |
| `api` | `com.github.blaxk3.api` | Thin HTTP client; reads the API key from config, makes `HttpURLConnection` GET requests, and parses JSON responses with Gson |

The three packages follow a classic **entry point → view → data source** flow:

```mermaid
flowchart LR
    User["User"] --> UI["UI Module
JFrame, JComboBox,
JTextField, JButton"]
    UI --> Converter["Converter Module
CurrencyConverter.main
SwingUtilities.invokeLater"]
    UI --> API["API Module
CurrencyRateAPI
HttpURLConnection, Gson"]
    API --> External["exchangerate-api.com"]
    UI --> SwingWorker["SwingWorker
CurrencyCode background tasks"]
    Converter --> UI
```

- **`converter`** does almost nothing beyond delegating to `SwingUtilities.invokeLater(UI::new)`. All application logic lives elsewhere, making it a safe place to add command-line argument parsing if needed in the future.
- **`ui`** assembles the `JFrame`, creates every visual component, and wires up listeners. It uses `SwingWorker` to fetch currency code lists from the API on background threads (avoiding EDT blocking), and delegates actual conversion to `CurrencyRateAPI` when the user clicks **Convert**. It also provides a custom `DocumentFilter` that restricts the amount field to digits and at most one decimal point.
- **`api`** is a single-class facade (`CurrencyRateAPI`) with five methods. It reads the API key from `config.properties`, builds URLs against the `https://v6.exchangerate-api.com/v6/` base, and parses JSON responses. It exposes two public operations: listing available ISO 4217 currency codes and converting an amount between two currencies.

## Key Patterns and Architecture

### Single-class API facade

The entire `api` package is one class with five methods — no interfaces, builders, or dependency injection. It is designed for straightforward, imperative use: instantiate `CurrencyRateAPI` and call methods directly. This simplicity works well for a desktop app with no concurrency requirements, but it comes with trade-offs.

### SwingWorker for background fetching

The `ui` module uses `SwingWorker<String[], Void>` (the `CurrencyCode` class) to fetch currency code lists off the EDT. `doInBackground()` performs the network call while `done()` pushes results back onto the EDT via the combo box's `addItem()` method. This is correct Swing usage, but it means both combo boxes spin up network requests simultaneously during window creation.

### Thin launcher pattern

The `converter` module's `CurrencyConverter.main()` is a textbook example of the Swing launcher pattern. It avoids constructing the UI in the main thread and instead queues it on the EDT. This is the correct pattern for all Java Swing applications.

### Input validation at the document level

`NumericFilter` is a `DocumentFilter` attached to the amount text field. It intercepts every insert and replace operation, reconstructing the full document content and validating it against the rule "digits only, at most one period." This prevents invalid input at the Swing component level rather than waiting for a Convert button action. The Convert button adds a secondary guard against the edge case where the field contains only a period.

### Error handling asymmetry

There is an inconsistency between how errors are handled in the two paths that call the API:

- **Currency code fetch (`CurrencyCode.done()`):** Catches `InterruptedException` and `ExecutionException`, logs them via SLF4J, and does **not** crash the application.
- **Manual conversion (Convert button):** Catches `MalformedURLException` and `URISyntaxException` but **rethrows** them as `RuntimeException`, which will crash the application if the network call fails.

This means an API outage will gracefully degrade during initial load but crash the app on a manual conversion.

### No connection pooling or timeouts

The `api` module uses raw `HttpURLConnection` without connection pooling, timeouts, or retry logic. Every call creates a fresh socket. This is acceptable for an interactive desktop app with low call frequency, but would be problematic if exposed on a web endpoint or called at high concurrency.

## Data flow

```mermaid
flowchart TD
    Main["CurrencyConverter.main"] --> Launch["SwingUtilities.invokeLater"]
    Launch --> UI["UI"]
    UI --> Swing["javax.swing"]
    UI --> API["CurrencyRateAPI"]
    API --> Config["config.properties
(API_KEY)"]
    UI --> SwingWorker["SwingWorker
CurrencyCode"]
    SwingWorker --> API
    API --> External["exchangerate-api.com
/pair/{from}/{to}/{amount}"]
    API --> Latest["exchangerate-api.com
/latest/USD"]
```

1. `CurrencyConverter.main()` queues the `UI` constructor on the EDT.
2. The `UI` constructor builds the window, attaches a `DocumentFilter` to the amount field, and launches two `CurrencyCode` background workers (one per combo box).
3. Each worker calls `CurrencyRateAPI.getCurrencyCode()`, which loads the API key from `config.properties`, builds the `/latest/USD` URL, and returns the currency code list.
4. The worker's `done()` method sorts the codes and populates the combo boxes on the EDT.
5. When the user enters an amount, selects currencies, and clicks **Convert**, `CurrencyRateAPI.convert()` is called with the source currency, target currency, and a `BigDecimal` amount.
6. The result is formatted with `DecimalFormat("#,###.###")` and displayed in the result label.

## Dependencies and Integration

### External dependencies

| Dependency | Used For |
|------------|----------|
| `com.google.gson:JsonObject, JsonParser` | Parsing API JSON responses |
| `org.slf4j:Logger, LoggerFactory` | Logging errors (missing config, background task exceptions) |
| `java.net.HttpURLConnection` | HTTP GET requests to exchangerate-api |
| `java.util.Properties` | Loading `config.properties` from the classpath |
| `java.math.BigDecimal` | Precise monetary amount representation |
| `javax.swing.SwingWorker` | Background thread for network calls |
| `javax.swing.text.DocumentFilter` | Input validation on the amount field |

### Configuration

This module expects a `config.properties` file on the classpath containing:

```properties
API_KEY=your_exchangerate_api_key_here
```

Place it at `src/main/resources/config.properties` in a standard Maven project layout.

### Integration points

`CurrencyRateAPI` has no framework dependencies and can be instantiated and used directly:

```java
CurrencyRateAPI api = new CurrencyRateAPI();
String[] currencies = api.getCurrencyCode();
String result = api.convert("USD", "JPY", new BigDecimal("100"));
```

The module does not depend on any other module outside this repository. All cross-package relationships are internal: `converter` depends on `ui`, and `ui` depends on `api`.

## Notes for Developers

### Thread safety

`CurrencyRateAPI` is effectively immutable (no mutable instance state), so it can be safely shared across threads. However, `HttpURLConnection` instances themselves are not thread-safe — do not share a connection object.

### Known risks and limitations

- **No connection or read timeouts**: A slow or unresponsive server will block the calling thread indefinitely. If this module is called from a web endpoint or user-facing thread, consider wrapping calls in a `CompletableFuture` with a timeout, or switch to OkHttp or Apache HttpClient.
- **`convert()` does not null-check**: If the API returns an error response (invalid currency codes, rate limit exceeded), `convert()` will throw a `NullPointerException` or `IllegalStateException` from Gson. Callers should handle exceptions defensively.
- **API key in URL**: The API key is embedded in the URL path, not in a header. Avoid logging the full URL in production.
- **No auto-refresh**: Currency codes are fetched only once during window creation. If the API adds or removes currencies, the user must restart the app.
- **Race condition on early conversion**: If the user clicks Convert before both `CurrencyCode` workers have finished populating the combo boxes, `getSelectedItem()` may return null and crash the application via `Objects.requireNonNull()`.
- **Free tier rate limits**: The free plan on exchangerate-api.com has rate limits. Caching the result from `getCurrencyCode()` is recommended since codes change infrequently.

### Extending the module

- **New API endpoints**: Add a public method to `CurrencyRateAPI` that builds a URL and calls `getJsonObject()`. Consider introducing a return type class to make the data easier to work with than raw `String` or `JsonObject`.
- **New UI features**: Add buttons by extending the `button()` method in `UI`. Create new `SwingWorker` subclasses similar to `CurrencyCode` for additional background operations.
- **New input types**: Subclass `NumericFilter` with different `isValid()` logic and attach via `setDocumentFilter()`.
- **Argument parsing**: Add command-line argument handling in `CurrencyConverter.main()` before the `invokeLater` call (e.g., to pre-select a currency pair).
