# (DD12) Business Logic — NumericFilter.replace() [12 LOC]

| Field | Value |
|-------|-------|
| Fully Qualified Name | `com.github.blaxk3.ui.NumericFilter` |
| Layer | Utility / UI Component (Package: `com.github.blaxk3.ui`) |
| Module | `ui` (Package: `com.github.blaxk3.ui`) |

## 1. Role

### NumericFilter.replace()

This method is part of a `DocumentFilter` implementation that enforces numeric-only input on a currency amount text field in a currency converter application. Its business purpose is to ensure that users can only enter valid numeric values — integers and decimal numbers with at most one decimal point — preventing malformed input from appearing in the currency conversion UI. It implements the **validation-through-simulation** pattern: instead of checking the typed character in isolation, it reconstructs the full document text as it would appear after the replacement, then delegates the validation decision to the `isValid()` helper method. Its role in the larger system is as a **shared input guard** attached to the currency amount `JTextField` via `setDocumentFilter()`. When a user types, pastes, or deletes text in the amount field, `replace()` is invoked by the Swing framework before the change is applied, acting as a gatekeeper that either permits the edit (by calling `super.replace()`) or silently blocks it. If the user also triggers an insertion (e.g., typing a character), the companion `insertString()` method performs the identical validation pattern. There are no conditional branches — all edits follow the same simulate-then-validate-then-allow-or-block path.

## 2. Processing Pattern (Detailed Business Logic)

```mermaid
flowchart TD
    START(["replace(params)"])
    CHECK_FILTERBYPASS["Evaluate fb"]
    GET_DOC["doc = fb.getDocument()"]
    INIT_SB["sb = new StringBuilder()"]
    APPEND_TEXT["sb.append(doc.getText(0, doc.getLength()))"]
    SIMULATE_REPLACE["sb.replace(offset, offset + length, text)"]
    VALIDATE["isValid(sb.toString())"]
    DIAMOND{isValid returns true?}
    ALLOW_REPLACE["super.replace(fb, offset, length, text, attrs)"]
    DENY_REPLACE["Reject input - do nothing"]
    END_NODE(["Return / Next"])

    START --> CHECK_FILTERBYPASS
    CHECK_FILTERBYPASS --> GET_DOC
    GET_DOC --> INIT_SB
    INIT_SB --> APPEND_TEXT
    APPEND_TEXT --> SIMULATE_REPLACE
    SIMULATE_REPLACE --> VALIDATE
    VALIDATE --> DIAMOND
    DIAMOND -->|true| ALLOW_REPLACE
    DIAMOND -->|false| DENY_REPLACE
    ALLOW_REPLACE --> END_NODE
    DENY_REPLACE --> END_NODE
```

**Requirements met:**
- All method calls (`fb.getDocument()`, `doc.getText()`, `sb.append()`, `sb.replace()`, `isValid()`, `super.replace()`) are represented as processing nodes.
- The conditional branch on `isValid()` is represented as a diamond with both `true` (allow) and `false` (reject) paths.
- No constants are used in this method — input validation is purely content-based.

## 3. Parameter Analysis

| No | Parameter Name | Type | Business Description |
|----|---------------|------|---------------------|
| 1 | `fb` | `FilterBypass` | The `FilterBypass` object provided by the Swing `PlainDocument` that allows the filter to bypass its own checks. It provides access to the underlying `Document` (text field content) and the `super.replace()` call to commit the change. |
| 2 | `offset` | `int` | The character position within the text field where the replacement begins. Represents the cursor position at which the user's edit is being applied. |
| 3 | `length` | `int` | The number of existing characters to remove at the given offset. Represents a deletion range — for example, if the user selects "12.50" and types "100", `length` is 5 (the selected text) and `text` is "100". If `length` is 0, this is an insertion rather than a replacement. |
| 4 | `text` | `String` | The new text the user is attempting to insert or paste. This is the content that will replace the range `[offset, offset + length)`. It carries the business data the user is entering (e.g., a currency amount like "123.45"). May be empty (for deletion). |
| 5 | `attrs` | `AttributeSet` | Swing text attribute metadata associated with the incoming text (e.g., font, color). Not used by this filter — it is simply forwarded to `super.replace()` for transparency. |

**External state read by this method:**
- `fb.getDocument()` — the `Document` backing the `JTextField`, representing the current full text content of the currency amount field. This is the only external state accessed; no instance fields of `NumericFilter` exist.

## 4. CRUD Operations / Called Services

| CRUD | SC / CBS | SC Code | Entity / DB | Operation Description |
|------|----------|---------|-------------|----------------------|
| R | `DocumentFilter.getDocument` | — | — | Reads the current text content of the Swing `Document` via `fb.getDocument()` to reconstruct the field's state. |
| R | `Document.getText` | — | — | Reads the full text of the document (`doc.getText(0, doc.getLength())`) to simulate the replacement result. |
| C | `StringBuilder.replace` | — | — | Creates a simulated version of the document text with the replacement applied (in-memory, not persisted). |
| R | `StringBuilder.toString` | — | — | Produces a String representation of the simulated text for validation. |
| R | `NumericFilter.isValid` | NumericFilter | — | Invokes the private validation method to check if the simulated text contains only digits and at most one decimal point. |
| U | `DocumentFilter.replace` (super) | — | — | Commits the replacement to the `Document` if validation passes. Updates the text field's content (in-memory UI state). |

Classification rationale:
- **R (Read):** `fb.getDocument()`, `doc.getText()` — both retrieve current document state for simulation. `isValid()` reads character data.
- **C (Create):** `StringBuilder.replace()` — builds a new text representation. Not a database operation; this is an in-memory simulation.
- **U (Update):** `super.replace()` — the only operation that modifies external state (the Swing document). This is the "commit" step that applies the validated edit.

No database tables or SC/CBS codes are involved. This method operates entirely within the UI layer, manipulating Swing's `Document` model.

## 5. Dependency Trace

| # | Caller (Screen/Batch) | Call Chain (Full Path to this Method) | Terminal (SC / CRUD / Entity) |
|---|----------------------|--------------------------------------|-------------------------------|
| 1 | UI framework (Swing) | `PlainDocument.replace` (internal Swing dispatch) -> `NumericFilter.replace(fb, offset, length, text, attrs)` | `isValid [R] in-memory text` |

**Caller analysis:** This method is not called directly by any application code. It is registered as a `DocumentFilter` on the currency amount `JTextField` (line 153: `setDocumentFilter(new NumericFilter())`), meaning the Swing `PlainDocument` framework invokes it automatically on every text modification (typed characters, pasted text, programmatic edits). No screen (KKSV*), batch, or CBS class calls this method directly.

**What this method calls (downstream):**
- `fb.getDocument()` — retrieves the owning `Document`
- `doc.getText()` — reads current document text
- `isValid()` — validates the simulated result (private method in same class)
- `super.replace()` — commits the change to the document (Swing framework)

## 6. Per-Branch Detail Blocks

**Block 1** — [EXEC] Document retrieval and simulation setup (L184-L185)

> Initialize local variables to access the document and prepare a `StringBuilder` for simulating the replacement.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `Document doc = fb.getDocument()` | // Retrieves the `Document` (text field content) from the `FilterBypass` |
| 2 | SET | `StringBuilder sb = new StringBuilder()` | // Creates empty builder for text simulation |

**Block 2** — [EXEC] Text simulation (L186-L187)

> Reconstruct the full document content in the `StringBuilder`, then apply the replacement operation virtually (without modifying the document).

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `sb.append(doc.getText(0, doc.getLength()))` | // Appends all current text from the document (reads the full currency amount field) |
| 2 | EXEC | `sb.replace(offset, offset + length, text)` | // Simulates the replacement: removes `length` chars at `offset` and inserts `text` |

**Block 3** — [IF] Validation check (L189-L192)

> If the simulated result passes numeric validation, commit the replacement to the document; otherwise silently reject the edit.

| # | Type | Code |
|---|------|------|
| 1 | EXEC | `isValid(sb.toString())` | // Validates the full simulated text is a valid numeric value |
| 2 | IF | **true** → `super.replace(fb, offset, length, text, attrs)` | // Commits the edit to the document — user sees the change |
| 3 | IF | **false** → (no-op) | // Edit is silently discarded — user sees no change |

**Block 3.1** — [NESTED] `isValid` validation (private method, called from Block 3)

> Validates whether a given string represents a valid numeric value for currency input: empty strings are allowed (to support deletion), non-empty strings must contain only digits and at most one decimal point.

| # | Type | Code |
|---|------|------|
| 1 | IF | `text.isEmpty()` — **true** → `return true` | // Empty text is valid (user deleted everything) |
| 2 | SET | `byte decimalCount = 0` | // Tracks decimal point occurrences |
| 3 | FOR | `for (int i = 0; i < text.length(); i++)` | // Scans each character |
| 4 | IF | `ch == '.'` && `decimalCount != 1` — **true** → `decimalCount++` | // First decimal point is allowed; second is rejected |
| 5 | IF | `ch == '.'` && `decimalCount == 1` — **false** → loop continues | // Second decimal point: falls through to else-if |
| 6 | IF | `!Character.isDigit(ch)` — **true** → `return false` | // Non-digit, non-decimal character: reject |
| 7 | RETURN | `return true` | // All characters are digits with at most one decimal point |

## 7. Glossary

| Term | Type | Business Meaning |
|------|------|------------------|
| `DocumentFilter` | Java Class | Swing API class that intercepts text modifications on a `Document` before they are applied. Used here to enforce numeric-only input on the currency amount field. |
| `FilterBypass` | Java Class | Swing API class passed to `DocumentFilter` methods, providing access to the underlying `Document` and the ability to commit changes via `super.replace()` / `super.insertString()`. |
| `Document` | Java Interface | Swing API interface representing the text model of a component (here, the `JTextField` content for the currency amount). |
| `AttributeSet` | Java Interface | Swing API interface carrying text attribute metadata (font, color). Not used by this filter; forwarded transparently. |
| `PlainDocument` | Java Class | Swing's default `Document` implementation. The text field's document is cast to `PlainDocument` when the `NumericFilter` is attached. |
| Currency Amount | Business Concept | The monetary value entered by the user for conversion. Must be a non-negative numeric value with optional decimal precision. |
| Simulate-then-validate | Design Pattern | Approach where the full post-edit text state is reconstructed in memory before deciding whether to permit the edit. Prevents partial or malformed states from ever appearing in the UI. |
| `isValid` | Method | Private validation method that checks if a string contains only digits and at most one decimal point. Empty strings are treated as valid (to support deleting all text). |
